Feat/lab settings (#7943)

Co-authored-by: Laurin <laurinquast@googlemail.com>
This commit is contained in:
Michael Skorokhodov 2026-04-16 12:58:10 +02:00 committed by GitHub
parent d7e7025624
commit 5a85fb9dd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 444 additions and 209 deletions

View file

@ -29,16 +29,17 @@
"peerDependencies": {
"@tanstack/react-form": "^1.23.8",
"date-fns": "^4.1.0",
"graphql-ws": "^6.0.6",
"lucide-react": "^0.548.0",
"lz-string": "^1.5.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"subscriptions-transport-ws": "^0.11.0",
"tslib": "^2.8.1",
"zod": "^4.1.12"
},
"dependencies": {
"@base-ui/react": "^1.1.0",
"@graphql-tools/url-loader": "^9.1.0",
"radix-ui": "^1.4.3",
"react-zoom-pan-pinch": "^3.7.0",
"uuid": "^13.0.0"
@ -98,7 +99,6 @@
"eslint-plugin-react-refresh": "^0.4.26",
"globals": "^16.5.0",
"graphql": "^16.12.0",
"graphql-ws": "^6.0.6",
"lodash": "^4.18.1",
"lucide-react": "^0.548.0",
"lz-string": "^1.5.0",
@ -114,6 +114,7 @@
"react-shadow": "^20.6.0",
"rollup-plugin-typescript2": "^0.36.0",
"sonner": "^2.0.7",
"subscriptions-transport-ws": "^0.11.0",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
"tailwindcss-scoped-preflight": "^3.5.7",

View file

@ -402,7 +402,7 @@ const LaboratoryContent = () => {
>
Preflight Script
</DropdownMenuItem>
{/* <DropdownMenuSeparator />
<DropdownMenuSeparator />
<DropdownMenuItem
onSelect={() => {
const tab =
@ -416,7 +416,7 @@ const LaboratoryContent = () => {
}}
>
Settings
</DropdownMenuItem> */}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<TooltipContent side="right">Settings</TooltipContent>
@ -514,7 +514,10 @@ export const Laboratory = (
const pluginsApi = usePlugins(props);
const testsApi = useTests(props);
const tabsApi = useTabs(props);
const endpointApi = useEndpoint(props);
const endpointApi = useEndpoint({
...props,
settingsApi,
});
const collectionsApi = useCollections({
...props,
tabsApi,

View file

@ -392,7 +392,7 @@ export const Response = ({ historyItem }: { historyItem?: LaboratoryHistoryReque
)}
{historyItem ? (
<div className="ml-auto flex items-center gap-2">
{historyItem?.status && (
{!!historyItem?.status && (
<Badge
className={cn('bg-green-400/10 text-green-500', {
'bg-red-400/10 text-red-500': isError,
@ -542,16 +542,31 @@ export const Query = (props: {
return;
}
const status = response.status;
const extensionsResponse = (response.extensions?.response as {
status: number;
headers: Record<string, string>;
}) ?? {
status: 0,
headers: {},
};
delete response.extensions?.request;
delete response.extensions?.response;
if (Object.keys(response.extensions ?? {}).length === 0) {
delete response.extensions;
}
const status = extensionsResponse.status;
const duration = performance.now() - startTime;
const responseText = await response.text();
const responseText = JSON.stringify(response, null, 2);
const size = responseText.length;
const newItemHistory = addHistory({
status,
duration,
size,
headers: JSON.stringify(Object.fromEntries(response.headers.entries()), null, 2),
headers: JSON.stringify(extensionsResponse.headers, null, 2),
operation,
preflightLogs: result?.logs ?? [],
response: responseText,

View file

@ -18,7 +18,6 @@ const settingsFormSchema = z.object({
protocol: z.enum(['SSE', 'GRAPHQL_SSE', 'WS', 'LEGACY_WS']),
}),
introspection: z.object({
queryName: z.string().optional(),
method: z.enum(['GET', 'POST']).optional(),
schemaDescription: z.boolean().optional(),
}),
@ -87,8 +86,12 @@ export const Settings = () => {
<Input
type="number"
name={field.name}
value={field.state.value}
onChange={e => field.handleChange(Number(e.target.value))}
value={field.state.value ?? ''}
onChange={e =>
field.handleChange(
e.target.value === '' ? undefined : Number(e.target.value),
)
}
/>
</Field>
);
@ -102,8 +105,12 @@ export const Settings = () => {
<Input
type="number"
name={field.name}
value={field.state.value}
onChange={e => field.handleChange(Number(e.target.value))}
value={field.state.value ?? ''}
onChange={e =>
field.handleChange(
e.target.value === '' ? undefined : Number(e.target.value),
)
}
/>
</Field>
);
@ -115,7 +122,7 @@ export const Settings = () => {
<Field className="flex-row items-center">
<Switch
className="!w-8"
checked={field.state.value}
checked={field.state.value ?? false}
onCheckedChange={field.handleChange}
/>
<FieldLabel htmlFor={field.name}>Use GET for queries</FieldLabel>
@ -175,20 +182,6 @@ export const Settings = () => {
</CardHeader>
<CardContent>
<FieldGroup>
<form.Field name="introspection.queryName">
{field => {
return (
<Field>
<FieldLabel htmlFor={field.name}>Query name</FieldLabel>
<Input
name={field.name}
value={field.state.value}
onChange={e => field.handleChange(e.target.value)}
/>
</Field>
);
}}
</form.Field>
<form.Field name="introspection.method">
{field => {
const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid;
@ -219,7 +212,7 @@ export const Settings = () => {
<Field className="flex-row items-center">
<Switch
className="!w-8"
checked={field.state.value}
checked={field.state.value ?? false}
onCheckedChange={field.handleChange}
/>
<FieldLabel htmlFor={field.name}>Schema description</FieldLabel>

View file

@ -1,13 +1,15 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import {
buildClientSchema,
getIntrospectionQuery,
GraphQLSchema,
introspectionFromSchema,
type IntrospectionQuery,
} from 'graphql';
import { toast } from 'sonner';
import z from 'zod';
// import z from 'zod';
import { asyncInterval } from '@/lib/utils';
import { SubscriptionProtocol, UrlLoader } from '@graphql-tools/url-loader';
import type { LaboratorySettingsActions, LaboratorySettingsState } from './settings';
export interface LaboratoryEndpointState {
endpoint: string | null;
@ -22,20 +24,11 @@ export interface LaboratoryEndpointActions {
restoreDefaultEndpoint: () => void;
}
const GraphQLResponseErrorSchema = z
.object({
errors: z.array(
z.object({
message: z.string(),
}),
),
})
.strict();
export const useEndpoint = (props: {
defaultEndpoint?: string | null;
onEndpointChange?: (endpoint: string | null) => void;
defaultSchemaIntrospection?: IntrospectionQuery | null;
settingsApi?: LaboratorySettingsState & LaboratorySettingsActions;
}): LaboratoryEndpointState & LaboratoryEndpointActions => {
const [endpoint, _setEndpoint] = useState<string | null>(props.defaultEndpoint ?? null);
const [introspection, setIntrospection] = useState<IntrospectionQuery | null>(null);
@ -52,6 +45,8 @@ export const useEndpoint = (props: {
return introspection ? buildClientSchema(introspection) : null;
}, [introspection]);
const loader = useMemo(() => new UrlLoader(), []);
const fetchSchema = useCallback(
async (signal?: AbortSignal) => {
if (endpoint === props.defaultEndpoint && props.defaultSchemaIntrospection) {
@ -65,28 +60,37 @@ export const useEndpoint = (props: {
}
try {
const response = await fetch(endpoint, {
signal,
method: 'POST',
body: JSON.stringify({
query: getIntrospectionQuery(),
}),
headers: {
'Content-Type': 'application/json',
},
}).then(r => r.json());
const result = await loader.load(endpoint, {
subscriptionsEndpoint: endpoint,
subscriptionsProtocol:
(props.settingsApi?.settings.subscriptions.protocol as SubscriptionProtocol) ??
SubscriptionProtocol.GRAPHQL_SSE,
credentials: props.settingsApi?.settings.fetch.credentials,
specifiedByUrl: true,
directiveIsRepeatable: true,
inputValueDeprecation: true,
retry: props.settingsApi?.settings.fetch.retry,
timeout: props.settingsApi?.settings.fetch.timeout,
useGETForQueries: props.settingsApi?.settings.fetch.useGETForQueries,
exposeHTTPDetailsInExtensions: true,
descriptions: props.settingsApi?.settings.introspection.schemaDescription ?? false,
method: props.settingsApi?.settings.introspection.method ?? 'POST',
fetch: (input: string | URL | Request, init?: RequestInit) =>
fetch(input, {
...init,
signal,
}),
});
const parsedResponse = GraphQLResponseErrorSchema.safeParse(response);
if (parsedResponse.success) {
throw new Error(parsedResponse.data.errors.map(e => e.message).join('\n'));
if (result.length === 0) {
throw new Error('Failed to fetch schema');
}
if (response.error && typeof response.error === 'string') {
throw new Error(response.error);
if (!result[0].schema) {
throw new Error('Failed to fetch schema');
}
setIntrospection(response.data as IntrospectionQuery);
setIntrospection(introspectionFromSchema(result[0].schema));
} catch (error: unknown) {
if (
error &&
@ -104,7 +108,12 @@ export const useEndpoint = (props: {
throw error;
}
},
[endpoint],
[
endpoint,
props.settingsApi?.settings.fetch.timeout,
props.settingsApi?.settings.introspection.method,
props.settingsApi?.settings.introspection.schemaDescription,
],
);
const shouldPollSchema = useMemo(() => {
@ -129,6 +138,7 @@ export const useEndpoint = (props: {
5000,
intervalController.signal,
);
return () => {
intervalController.abort();
};

View file

@ -1,8 +1,16 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import type { GraphQLSchema } from 'graphql';
import { createClient } from 'graphql-ws';
import {
DocumentNode,
ExecutionResult,
getOperationAST,
Kind,
parse,
type GraphQLSchema,
} from 'graphql';
import { decompressFromEncodedURIComponent } from 'lz-string';
import { v4 as uuidv4 } from 'uuid';
import { isAsyncIterable } from '@/lib/utils';
import { SubscriptionProtocol, UrlLoader } from '@graphql-tools/url-loader';
import { LaboratoryPermission, LaboratoryPermissions } from '../components/laboratory/context';
import type {
LaboratoryCollectionOperation,
@ -23,6 +31,14 @@ import type { LaboratoryPreflightActions, LaboratoryPreflightState } from './pre
import type { LaboratorySettingsActions, LaboratorySettingsState } from './settings';
import type { LaboratoryTabOperation, LaboratoryTabsActions, LaboratoryTabsState } from './tabs';
function getOperationType(query: string): 'query' | 'mutation' | 'subscription' | null {
try {
return getOperationAST(parse(query))?.operation ?? null;
} catch {
return null;
}
}
export interface LaboratoryOperation {
id: string;
name: string;
@ -66,7 +82,7 @@ export interface LaboratoryOperationsActions {
operationName?: string;
onResponse?: (response: string) => void;
},
) => Promise<Response | null>;
) => Promise<ExecutionResult | null>;
stopActiveOperation: (() => void) | null;
isActiveOperationLoading: boolean;
isOperationLoading: (operationId: string) => boolean;
@ -80,6 +96,27 @@ export interface LaboratoryOperationsCallbacks {
onOperationDelete?: (operation: LaboratoryOperation) => void;
}
const getOperationWithFragments = (
document: DocumentNode,
operationName?: string,
): DocumentNode => {
const definitions = document.definitions.filter(definition => {
if (
definition.kind === Kind.OPERATION_DEFINITION &&
operationName &&
definition.name?.value !== operationName
) {
return false;
}
return true;
});
return {
kind: Kind.DOCUMENT,
definitions,
};
};
export const useOperations = (
props: {
checkPermissions: (
@ -326,6 +363,8 @@ export const useOperations = (
return activeOperation ? isOperationLoading(activeOperation.id) : false;
}, [activeOperation, isOperationLoading]);
const loader = useMemo(() => new UrlLoader(), []);
const runActiveOperation = useCallback(
async (
endpoint: string,
@ -337,7 +376,7 @@ export const useOperations = (
},
plugins: LaboratoryPlugin[] = props.pluginsApi?.plugins ?? [],
pluginsState: Record<string, any> = props.pluginsApi?.pluginsState ?? {},
) => {
): Promise<ExecutionResult | null> => {
if (!activeOperation?.query) {
return null;
}
@ -393,101 +432,76 @@ export const useOperations = (
)
: {};
if (activeOperation.query.startsWith('subscription')) {
const client = createClient({
url: endpoint.replace('http', 'ws'),
connectionParams: {
...mergedHeaders,
},
});
const executor = loader.getExecutorAsync(endpoint, {
subscriptionsEndpoint: endpoint,
subscriptionsProtocol:
(props.settingsApi?.settings.subscriptions.protocol as SubscriptionProtocol) ??
SubscriptionProtocol.GRAPHQL_SSE,
credentials: props.settingsApi?.settings.fetch.credentials,
specifiedByUrl: true,
directiveIsRepeatable: true,
inputValueDeprecation: true,
retry: props.settingsApi?.settings.fetch.retry,
timeout: props.settingsApi?.settings.fetch.timeout,
useGETForQueries: props.settingsApi?.settings.fetch.useGETForQueries,
exposeHTTPDetailsInExtensions: true,
fetch,
});
client.on('connected', () => {
console.log('connected');
});
client.on('error', () => {
setStopOperationsFunctions(prev => {
const newStopOperationsFunctions = { ...prev };
delete newStopOperationsFunctions[activeOperation.id];
return newStopOperationsFunctions;
});
});
client.on('closed', () => {
setStopOperationsFunctions(prev => {
const newStopOperationsFunctions = { ...prev };
delete newStopOperationsFunctions[activeOperation.id];
return newStopOperationsFunctions;
});
});
client.subscribe(
{
query: activeOperation.query,
variables,
extensions,
},
{
next: message => {
options?.onResponse?.(JSON.stringify(message ?? {}));
},
error: () => {},
complete: () => {},
},
);
setStopOperationsFunctions(prev => ({
...prev,
[activeOperation.id]: () => {
void client.dispose();
setStopOperationsFunctions(prev => {
const newStopOperationsFunctions = { ...prev };
delete newStopOperationsFunctions[activeOperation.id];
return newStopOperationsFunctions;
});
},
}));
return Promise.resolve(new Response());
}
const document = getOperationWithFragments(parse(activeOperation.query));
const abortController = new AbortController();
const response = fetch(endpoint, {
method: 'POST',
credentials: props.settingsApi?.settings.fetch.credentials,
body: JSON.stringify({
query: activeOperation.query,
operationName: options?.operationName,
variables,
extensions,
}),
headers: {
...mergedHeaders,
'Content-Type': 'application/json',
},
signal: abortController.signal,
}).finally(() => {
setStopOperationsFunctions(prev => {
const newStopOperationsFunctions = { ...prev };
delete newStopOperationsFunctions[activeOperation.id];
return newStopOperationsFunctions;
});
});
setStopOperationsFunctions(prev => ({
...prev,
[activeOperation.id]: () => abortController.abort(),
[activeOperation.id]: () => {
abortController.abort();
},
}));
const response = await executor({
document,
variables,
extensions: {
...extensions,
headers: mergedHeaders,
},
signal: abortController.signal,
});
if (isAsyncIterable(response)) {
try {
for await (const item of response) {
options?.onResponse?.(JSON.stringify(item ?? {}));
}
} finally {
setStopOperationsFunctions(prev => {
const newStopOperationsFunctions = { ...prev };
delete newStopOperationsFunctions[activeOperation.id];
return newStopOperationsFunctions;
});
}
return null;
}
if (response.extensions?.response?.body) {
delete response.extensions.response.body;
}
setStopOperationsFunctions(prev => {
const newStopOperationsFunctions = { ...prev };
delete newStopOperationsFunctions[activeOperation.id];
return newStopOperationsFunctions;
});
return response;
},
[activeOperation, props.preflightApi, props.envApi, props.pluginsApi],
[activeOperation, props.preflightApi, props.envApi, props.pluginsApi, props.settingsApi],
);
const isOperationSubscription = useCallback((operation: LaboratoryOperation) => {
return operation.query?.startsWith('subscription') ?? false;
return getOperationType(operation.query) === 'subscription';
}, []);
const isActiveOperationSubscription = useMemo(() => {

View file

@ -11,12 +11,48 @@ export type LaboratorySettings = {
protocol: 'SSE' | 'GRAPHQL_SSE' | 'WS' | 'LEGACY_WS';
};
introspection: {
queryName?: string;
method?: 'GET' | 'POST';
schemaDescription?: boolean;
};
};
export const defaultLaboratorySettings: LaboratorySettings = {
fetch: {
credentials: 'same-origin',
timeout: 10000,
retry: 3,
useGETForQueries: false,
},
subscriptions: {
protocol: 'WS',
},
introspection: {
method: 'POST',
schemaDescription: false,
},
};
export const normalizeLaboratorySettings = (
settings?: Partial<LaboratorySettings> | null,
): LaboratorySettings => ({
fetch: {
credentials: settings?.fetch?.credentials ?? defaultLaboratorySettings.fetch.credentials,
timeout: settings?.fetch?.timeout ?? defaultLaboratorySettings.fetch.timeout,
retry: settings?.fetch?.retry ?? defaultLaboratorySettings.fetch.retry,
useGETForQueries:
settings?.fetch?.useGETForQueries ?? defaultLaboratorySettings.fetch.useGETForQueries,
},
subscriptions: {
protocol: settings?.subscriptions?.protocol ?? defaultLaboratorySettings.subscriptions.protocol,
},
introspection: {
method: settings?.introspection?.method ?? defaultLaboratorySettings.introspection.method,
schemaDescription:
settings?.introspection?.schemaDescription ??
defaultLaboratorySettings.introspection.schemaDescription,
},
});
export interface LaboratorySettingsState {
settings: LaboratorySettings;
}
@ -30,28 +66,14 @@ export const useSettings = (props: {
onSettingsChange?: (settings: LaboratorySettings | null) => void;
}): LaboratorySettingsState & LaboratorySettingsActions => {
const [settings, _setSettings] = useState<LaboratorySettings>(
props.defaultSettings ?? {
fetch: {
credentials: 'same-origin',
timeout: 10000,
retry: 3,
useGETForQueries: false,
},
subscriptions: {
protocol: 'WS',
},
introspection: {
queryName: 'IntrospectionQuery',
method: 'POST',
schemaDescription: false,
},
},
normalizeLaboratorySettings(props.defaultSettings),
);
const setSettings = useCallback(
(settings: LaboratorySettings) => {
_setSettings(settings);
props.onSettingsChange?.(settings);
const normalizedSettings = normalizeLaboratorySettings(settings);
_setSettings(normalizedSettings);
props.onSettingsChange?.(normalizedSettings);
},
[props],
);

View file

@ -40,3 +40,7 @@ export async function asyncInterval(
});
}
}
export function isAsyncIterable<T>(val: unknown): val is AsyncIterable<T> {
return typeof Object(val)[Symbol.asyncIterator] === 'function';
}

View file

@ -1,4 +1,5 @@
import type { GraphiQLOptions } from 'graphql-yoga';
import type { LaboratoryProps } from '@graphql-hive/laboratory';
import {
editorWorkerService,
favicon,
@ -9,6 +10,30 @@ import {
typescriptWorker,
} from './laboratory.js';
const mapGraphiQLOptionsToLaboratoryProps = (opts?: GraphiQLOptions): LaboratoryProps => {
if (!opts) {
return {};
}
return {
defaultSettings: {
fetch: {
credentials: opts.credentials ?? 'same-origin',
timeout: opts.timeout,
retry: opts.retry,
useGETForQueries: opts.useGETForQueries,
},
subscriptions: {
protocol: opts.subscriptionsProtocol ?? 'WS',
},
introspection: {
method: opts.method,
schemaDescription: opts.schemaDescription,
},
},
} satisfies LaboratoryProps;
};
export const renderLaboratory = (opts?: GraphiQLOptions) => /* HTML */ `
<!doctype html>
<html lang="en">
@ -63,7 +88,10 @@ export const renderLaboratory = (opts?: GraphiQLOptions) => /* HTML */ `
${js};
HiveLaboratory.renderLaboratory(window.document.querySelector('#root'));
HiveLaboratory.renderLaboratory(
window.document.querySelector('#root'),
${JSON.stringify(mapGraphiQLOptionsToLaboratoryProps(opts))},
);
</script>
</body>
</html>

View file

@ -596,11 +596,7 @@ function useLaboratoryState(props: {
defaultHistory: historyData ?? [],
defaultTabs,
defaultActiveTabId: getLocalStorageState('activeTabId', null),
defaultSettings: getLocalStorageState('settings', {
fetch: {
credentials: 'same-origin',
},
}),
defaultSettings: getLocalStorageState('settings', null),
defaultPreflight: preflight?.preflightScript?.sourceCode
? {
script: preflight.preflightScript.sourceCode,

View file

@ -675,6 +675,9 @@ importers:
'@base-ui/react':
specifier: ^1.1.0
version: 1.1.0(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@graphql-tools/url-loader':
specifier: ^9.1.0
version: 9.1.0(@types/node@24.12.2)(graphql@16.12.0)
radix-ui:
specifier: ^1.4.3
version: 1.4.3(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -847,9 +850,6 @@ importers:
graphql:
specifier: ^16.12.0
version: 16.12.0
graphql-ws:
specifier: ^6.0.6
version: 6.0.6(graphql@16.12.0)(ws@8.18.0)
lodash:
specifier: ^4.17.23
version: 4.18.1
@ -895,6 +895,9 @@ importers:
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
subscriptions-transport-ws:
specifier: ^0.11.0
version: 0.11.0(graphql@16.12.0)
tailwind-merge:
specifier: ^3.4.0
version: 3.4.0
@ -983,7 +986,7 @@ importers:
version: 1.0.9(pino@10.3.0)
'@graphql-yoga/plugin-persisted-operations':
specifier: ^3.9.0
version: 3.9.0(@graphql-tools/utils@11.0.0(graphql@16.9.0))(graphql-yoga@5.13.3(graphql@16.9.0))(graphql@16.9.0)
version: 3.9.0(@graphql-tools/utils@11.0.1-alpha-20260403104244-04c5cf56aba6702e37b7efb722f6b5158f5a9de2(graphql@16.9.0))(graphql-yoga@5.13.3(graphql@16.9.0))(graphql@16.9.0)
graphql:
specifier: ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
version: 16.9.0
@ -4748,6 +4751,12 @@ packages:
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor-common@1.0.6':
resolution: {integrity: sha512-23/K5C+LSlHDI0mj2SwCJ33RcELCcyDUgABm1Z8St7u/4Z5+95i925H/NAjUyggRjiaY8vYtNiMOPE49aPX1sg==}
engines: {node: '>=20.0.0'}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor-graphql-ws@0.0.14':
resolution: {integrity: sha512-P2nlkAsPZKLIXImFhj0YTtny5NQVGSsKnhi7PzXiaHSXc6KkzqbWZHKvikD4PObanqg+7IO58rKFpGXP7eeO+w==}
peerDependencies:
@ -4777,6 +4786,12 @@ packages:
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor-graphql-ws@3.1.5':
resolution: {integrity: sha512-WXRsfwu9AkrORD9nShrd61OwwxeQ5+eXYcABRR3XPONFIS8pWQfDJGGqxql9/227o/s0DV5SIfkBURb5Knzv+A==}
engines: {node: '>=20.0.0'}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor-http@0.1.9':
resolution: {integrity: sha512-tNzMt5qc1ptlHKfpSv9wVBVKCZ7gks6Yb/JcYJluxZIT4qRV+TtOFjpptfBU63usgrGVOVcGjzWc/mt7KhmmpQ==}
peerDependencies:
@ -4800,6 +4815,12 @@ packages:
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor-http@3.2.1':
resolution: {integrity: sha512-53i0TYO0cznIlZDJcnq4gQ6SOZ8efGgCDV33MYh6oqEapcp36tCMEVnVGVxcX5qRRyNHkqTY6hkA+/AyK9kicQ==}
engines: {node: '>=20.0.0'}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor-legacy-ws@0.0.11':
resolution: {integrity: sha512-4ai+NnxlNfvIQ4c70hWFvOZlSUN8lt7yc+ZsrwtNFbFPH/EroIzFMapAxM9zwyv9bH38AdO3TQxZ5zNxgBdvUw==}
peerDependencies:
@ -4817,6 +4838,12 @@ packages:
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor-legacy-ws@1.1.26':
resolution: {integrity: sha512-rlFHk8XoRCXjARQAlHTgtisyE5KJxMb9UyR4hRbD6tLlYjmzNf9ms8GjsLYe/j1QpHJ7fNDm9aXqj1+evhQ/MQ==}
engines: {node: '>=16.0.0'}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/executor@0.0.17':
resolution: {integrity: sha512-DVKyMclsNY8ei14FUrR4jn24VHB3EuFldD8yGWrcJ8cudSh47sknznvXN6q0ffqDeAf0IlZSaBCHrOTBqA7OfQ==}
peerDependencies:
@ -5084,6 +5111,12 @@ packages:
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/url-loader@9.1.0':
resolution: {integrity: sha512-G3Ul5sLsLOJlfT4LkdQSKcHoJ+4CuSeUbRT1XjBXZSgNkeXZt2MXHJQX0X8+b4mJq7fI3thcfbiB+5sEUlnT7g==}
engines: {node: '>=20.0.0'}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/utils@10.11.0':
resolution: {integrity: sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==}
engines: {node: '>=16.0.0'}
@ -5114,6 +5147,12 @@ packages:
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/utils@11.0.1-alpha-20260403104244-04c5cf56aba6702e37b7efb722f6b5158f5a9de2':
resolution: {integrity: sha512-8uABmdEMr7gS7hOcEO0p6Y3cQZXNREE/4YWcSZWPuDSDU90rGJscwMlTcgW3zFwb3qqJ9ZpskQRq4ae8gMpa1Q==}
engines: {node: '>=16.0.0'}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/utils@9.2.1':
resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==}
peerDependencies:
@ -5131,12 +5170,6 @@ packages:
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/wrap@11.0.5':
resolution: {integrity: sha512-W0lm1AWLUAF2uKyrplC5PmyVJGCD/n7HO/R+bxoAIGQlZ2ESTbdB1DqZalMUF3D6AwmdzgTsDgoZBJgndnWs9g==}
engines: {node: '>=20.0.0'}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
'@graphql-tools/wrap@11.1.2':
resolution: {integrity: sha512-TcKZzUzJNmuyMBQ1oMdnxhBUUacN/5VEJu0/1KVce2aIzCwTTaN9JTU3MgjO7l5Ixn4QLkc6XbxYNv0cHDQgtQ==}
engines: {node: '>=20.0.0'}
@ -11125,6 +11158,9 @@ packages:
babel-plugin-react-compiler@19.1.0-rc.3:
resolution: {integrity: sha512-mjRn69WuTz4adL0bXGx8Rsyk1086zFJeKmes6aK0xPuK3aaXmDJdLHqwKKMrpm6KAI1MCoUK72d2VeqQbu8YIA==}
backo2@1.0.2:
resolution: {integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==}
bail@2.0.2:
resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
@ -12796,6 +12832,9 @@ packages:
eventemitter2@6.4.7:
resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==}
eventemitter3@3.1.2:
resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==}
eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
@ -14361,6 +14400,9 @@ packages:
isstream@0.1.2:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
iterall@1.3.0:
resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==}
iterator.prototype@1.1.5:
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
engines: {node: '>= 0.4'}
@ -17856,6 +17898,12 @@ packages:
stylis@4.1.3:
resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==}
subscriptions-transport-ws@0.11.0:
resolution: {integrity: sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==}
deprecated: The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md
peerDependencies:
graphql: ^15.7.2 || ^16.0.0
sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
engines: {node: '>=16 || 14 >=14.17'}
@ -17901,6 +17949,14 @@ packages:
swap-case@2.0.2:
resolution: {integrity: sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==}
symbol-observable@1.2.0:
resolution: {integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==}
engines: {node: '>=0.10.0'}
sync-fetch@0.6.0:
resolution: {integrity: sha512-IELLEvzHuCfc1uTsshPK58ViSdNqXxlml1U+fmwJIKLYKOr/rAtBrorE2RYm5IHaMpDNlmC0fr1LAvdXvyheEQ==}
engines: {node: '>=18'}
sync-fetch@0.6.0-2:
resolution: {integrity: sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A==}
engines: {node: '>=18'}
@ -23669,7 +23725,7 @@ snapshots:
'@graphql-tools/batch-delegate': 10.0.5(graphql@16.12.0)
'@graphql-tools/delegate': 11.1.3(graphql@16.12.0)
'@graphql-tools/utils': 10.9.1(graphql@16.12.0)
'@graphql-tools/wrap': 11.0.5(graphql@16.12.0)
'@graphql-tools/wrap': 11.1.2(graphql@16.12.0)
'@whatwg-node/disposablestack': 0.0.6
'@whatwg-node/fetch': 0.10.13
'@whatwg-node/promise-helpers': 1.3.2
@ -23693,7 +23749,7 @@ snapshots:
'@graphql-tools/batch-delegate': 10.0.5(graphql@16.12.0)
'@graphql-tools/delegate': 11.1.3(graphql@16.12.0)
'@graphql-tools/utils': 10.9.1(graphql@16.12.0)
'@graphql-tools/wrap': 11.0.5(graphql@16.12.0)
'@graphql-tools/wrap': 11.1.2(graphql@16.12.0)
'@whatwg-node/disposablestack': 0.0.6
'@whatwg-node/fetch': 0.10.13
'@whatwg-node/promise-helpers': 1.3.2
@ -23717,7 +23773,7 @@ snapshots:
'@graphql-tools/batch-delegate': 10.0.5(graphql@16.9.0)
'@graphql-tools/delegate': 11.1.3(graphql@16.9.0)
'@graphql-tools/utils': 10.9.1(graphql@16.9.0)
'@graphql-tools/wrap': 11.0.5(graphql@16.9.0)
'@graphql-tools/wrap': 11.1.2(graphql@16.9.0)
'@whatwg-node/disposablestack': 0.0.6
'@whatwg-node/fetch': 0.10.13
'@whatwg-node/promise-helpers': 1.3.2
@ -24001,6 +24057,12 @@ snapshots:
'@graphql-tools/utils': 10.11.0(graphql@16.9.0)
graphql: 16.9.0
'@graphql-tools/executor-common@1.0.6(graphql@16.12.0)':
dependencies:
'@envelop/core': 5.5.1
'@graphql-tools/utils': 11.0.0(graphql@16.12.0)
graphql: 16.12.0
'@graphql-tools/executor-graphql-ws@0.0.14(graphql@16.9.0)':
dependencies:
'@graphql-tools/utils': 9.2.1(graphql@16.9.0)
@ -24077,6 +24139,23 @@ snapshots:
- uWebSockets.js
- utf-8-validate
'@graphql-tools/executor-graphql-ws@3.1.5(graphql@16.12.0)':
dependencies:
'@graphql-tools/executor-common': 1.0.6(graphql@16.12.0)
'@graphql-tools/utils': 11.0.0(graphql@16.12.0)
'@whatwg-node/disposablestack': 0.0.6
graphql: 16.12.0
graphql-ws: 6.0.6(graphql@16.12.0)(ws@8.18.0)
isows: 1.0.7(ws@8.18.0)
tslib: 2.8.1
ws: 8.18.0
transitivePeerDependencies:
- '@fastify/websocket'
- bufferutil
- crossws
- uWebSockets.js
- utf-8-validate
'@graphql-tools/executor-http@0.1.9(@types/node@24.12.2)(graphql@16.9.0)':
dependencies:
'@graphql-tools/utils': 9.2.1(graphql@16.9.0)
@ -24129,7 +24208,7 @@ snapshots:
'@whatwg-node/fetch': 0.10.13
'@whatwg-node/promise-helpers': 1.3.2
graphql: 16.9.0
meros: 1.2.1(@types/node@24.12.2)
meros: 1.3.2(@types/node@24.12.2)
tslib: 2.8.1
transitivePeerDependencies:
- '@types/node'
@ -24179,6 +24258,21 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
'@graphql-tools/executor-http@3.2.1(@types/node@24.12.2)(graphql@16.12.0)':
dependencies:
'@graphql-hive/signal': 2.0.0
'@graphql-tools/executor-common': 1.0.6(graphql@16.12.0)
'@graphql-tools/utils': 11.0.0(graphql@16.12.0)
'@repeaterjs/repeater': 3.0.6
'@whatwg-node/disposablestack': 0.0.6
'@whatwg-node/fetch': 0.10.13
'@whatwg-node/promise-helpers': 1.3.2
graphql: 16.12.0
meros: 1.3.2(@types/node@24.12.2)
tslib: 2.8.1
transitivePeerDependencies:
- '@types/node'
'@graphql-tools/executor-legacy-ws@0.0.11(graphql@16.9.0)':
dependencies:
'@graphql-tools/utils': 9.2.1(graphql@16.9.0)
@ -24215,6 +24309,18 @@ snapshots:
- bufferutil
- utf-8-validate
'@graphql-tools/executor-legacy-ws@1.1.26(graphql@16.12.0)':
dependencies:
'@graphql-tools/utils': 11.0.0(graphql@16.12.0)
'@types/ws': 8.5.3
graphql: 16.12.0
isomorphic-ws: 5.0.0(ws@8.18.0)
tslib: 2.8.1
ws: 8.18.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@graphql-tools/executor@0.0.17(graphql@16.9.0)':
dependencies:
'@graphql-tools/utils': 9.2.1(graphql@16.9.0)
@ -24868,6 +24974,29 @@ snapshots:
- uWebSockets.js
- utf-8-validate
'@graphql-tools/url-loader@9.1.0(@types/node@24.12.2)(graphql@16.12.0)':
dependencies:
'@graphql-tools/executor-graphql-ws': 3.1.5(graphql@16.12.0)
'@graphql-tools/executor-http': 3.2.1(@types/node@24.12.2)(graphql@16.12.0)
'@graphql-tools/executor-legacy-ws': 1.1.26(graphql@16.12.0)
'@graphql-tools/utils': 11.0.0(graphql@16.12.0)
'@graphql-tools/wrap': 11.1.2(graphql@16.12.0)
'@types/ws': 8.5.3
'@whatwg-node/fetch': 0.10.13
'@whatwg-node/promise-helpers': 1.3.2
graphql: 16.12.0
isomorphic-ws: 5.0.0(ws@8.18.0)
sync-fetch: 0.6.0
tslib: 2.8.1
ws: 8.18.0
transitivePeerDependencies:
- '@fastify/websocket'
- '@types/node'
- bufferutil
- crossws
- uWebSockets.js
- utf-8-validate
'@graphql-tools/utils@10.11.0(graphql@16.12.0)':
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0)
@ -24919,6 +25048,14 @@ snapshots:
graphql: 16.9.0
tslib: 2.8.1
'@graphql-tools/utils@11.0.0(graphql@16.12.0)':
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0)
'@whatwg-node/promise-helpers': 1.3.2
cross-inspect: 1.0.1
graphql: 16.12.0
tslib: 2.8.1
'@graphql-tools/utils@11.0.0(graphql@16.9.0)':
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0)
@ -24927,6 +25064,14 @@ snapshots:
graphql: 16.9.0
tslib: 2.8.1
'@graphql-tools/utils@11.0.1-alpha-20260403104244-04c5cf56aba6702e37b7efb722f6b5158f5a9de2(graphql@16.9.0)':
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0)
'@whatwg-node/promise-helpers': 1.3.2
cross-inspect: 1.0.1
graphql: 16.9.0
tslib: 2.8.1
'@graphql-tools/utils@9.2.1(graphql@16.9.0)':
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0)
@ -24951,24 +25096,6 @@ snapshots:
graphql: 16.9.0
tslib: 2.8.1
'@graphql-tools/wrap@11.0.5(graphql@16.12.0)':
dependencies:
'@graphql-tools/delegate': 11.1.3(graphql@16.12.0)
'@graphql-tools/schema': 10.0.29(graphql@16.12.0)
'@graphql-tools/utils': 10.11.0(graphql@16.12.0)
'@whatwg-node/promise-helpers': 1.3.2
graphql: 16.12.0
tslib: 2.8.1
'@graphql-tools/wrap@11.0.5(graphql@16.9.0)':
dependencies:
'@graphql-tools/delegate': 11.1.3(graphql@16.9.0)
'@graphql-tools/schema': 10.0.29(graphql@16.9.0)
'@graphql-tools/utils': 10.11.0(graphql@16.9.0)
'@whatwg-node/promise-helpers': 1.3.2
graphql: 16.9.0
tslib: 2.8.1
'@graphql-tools/wrap@11.1.2(graphql@16.12.0)':
dependencies:
'@graphql-tools/delegate': 12.0.2(graphql@16.12.0)
@ -25117,9 +25244,9 @@ snapshots:
graphql: 16.9.0
graphql-yoga: 5.17.1(graphql@16.9.0)
'@graphql-yoga/plugin-persisted-operations@3.9.0(@graphql-tools/utils@11.0.0(graphql@16.9.0))(graphql-yoga@5.13.3(graphql@16.9.0))(graphql@16.9.0)':
'@graphql-yoga/plugin-persisted-operations@3.9.0(@graphql-tools/utils@11.0.1-alpha-20260403104244-04c5cf56aba6702e37b7efb722f6b5158f5a9de2(graphql@16.9.0))(graphql-yoga@5.13.3(graphql@16.9.0))(graphql@16.9.0)':
dependencies:
'@graphql-tools/utils': 11.0.0(graphql@16.9.0)
'@graphql-tools/utils': 11.0.1-alpha-20260403104244-04c5cf56aba6702e37b7efb722f6b5158f5a9de2(graphql@16.9.0)
graphql: 16.9.0
graphql-yoga: 5.13.3(graphql@16.9.0)
@ -32323,6 +32450,8 @@ snapshots:
dependencies:
'@babel/types': 7.28.5
backo2@1.0.2: {}
bail@2.0.2: {}
balanced-match@1.0.2: {}
@ -34321,6 +34450,8 @@ snapshots:
eventemitter2@6.4.7: {}
eventemitter3@3.1.2: {}
eventemitter3@4.0.7: {}
eventemitter3@5.0.1: {}
@ -36293,6 +36424,8 @@ snapshots:
isstream@0.1.2: {}
iterall@1.3.0: {}
iterator.prototype@1.1.5:
dependencies:
define-data-property: 1.1.4
@ -37202,10 +37335,6 @@ snapshots:
merge2@1.4.1: {}
meros@1.2.1(@types/node@24.12.2):
optionalDependencies:
'@types/node': 24.12.2
meros@1.2.1(@types/node@25.5.0):
optionalDependencies:
'@types/node': 25.5.0
@ -40515,6 +40644,18 @@ snapshots:
stylis@4.1.3: {}
subscriptions-transport-ws@0.11.0(graphql@16.12.0):
dependencies:
backo2: 1.0.2
eventemitter3: 3.1.2
graphql: 16.12.0
iterall: 1.3.0
symbol-observable: 1.2.0
ws: 8.18.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
sucrase@3.35.0:
dependencies:
'@jridgewell/gen-mapping': 0.3.13
@ -40567,6 +40708,14 @@ snapshots:
dependencies:
tslib: 2.8.1
symbol-observable@1.2.0: {}
sync-fetch@0.6.0:
dependencies:
node-fetch: 3.3.2
timeout-signal: 2.0.0
whatwg-mimetype: 4.0.0
sync-fetch@0.6.0-2:
dependencies:
node-fetch: 3.3.2