From 2fa8a55bbed18235e0f4491c3fd789136944a4bd Mon Sep 17 00:00:00 2001 From: Mykhailo Skorokhodov Date: Tue, 5 May 2026 21:32:28 +0200 Subject: [PATCH 1/4] lab to append active operation headers to introspection header --- .../src/components/laboratory/laboratory.tsx | 12 +- .../src/components/laboratory/settings.tsx | 1 + .../libraries/laboratory/src/lib/endpoint.ts | 171 +++++++++++------- 3 files changed, 115 insertions(+), 69 deletions(-) diff --git a/packages/libraries/laboratory/src/components/laboratory/laboratory.tsx b/packages/libraries/laboratory/src/components/laboratory/laboratory.tsx index 44c887be38..0f6dd1ad6d 100644 --- a/packages/libraries/laboratory/src/components/laboratory/laboratory.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/laboratory.tsx @@ -514,15 +514,10 @@ export const Laboratory = ( const pluginsApi = usePlugins(props); const testsApi = useTests(props); const tabsApi = useTabs(props); - const endpointApi = useEndpoint({ - ...props, - settingsApi, - }); const collectionsApi = useCollections({ ...props, tabsApi, }); - const operationsApi = useOperations({ ...props, collectionsApi, @@ -533,6 +528,13 @@ export const Laboratory = ( pluginsApi, checkPermissions, }); + const endpointApi = useEndpoint({ + ...props, + settingsApi, + operationsApi, + envApi, + pluginsApi, + }); const historyApi = useHistory(props); diff --git a/packages/libraries/laboratory/src/components/laboratory/settings.tsx b/packages/libraries/laboratory/src/components/laboratory/settings.tsx index 92f6a9802f..a335481692 100644 --- a/packages/libraries/laboratory/src/components/laboratory/settings.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/settings.tsx @@ -20,6 +20,7 @@ const settingsFormSchema = z.object({ introspection: z.object({ method: z.enum(['GET', 'POST']).optional(), schemaDescription: z.boolean().optional(), + headers: z.string().optional(), }), }); diff --git a/packages/libraries/laboratory/src/lib/endpoint.ts b/packages/libraries/laboratory/src/lib/endpoint.ts index 2e653ff3fe..25ceb64037 100644 --- a/packages/libraries/laboratory/src/lib/endpoint.ts +++ b/packages/libraries/laboratory/src/lib/endpoint.ts @@ -5,7 +5,12 @@ import { introspectionFromSchema, type IntrospectionQuery, } from 'graphql'; +import { throttle } from 'lodash'; import { toast } from 'sonner'; +import { LaboratoryEnv, LaboratoryEnvActions, LaboratoryEnvState } from '@/lib/env'; +import { LaboratoryOperationsActions, LaboratoryOperationsState } from '@/lib/operations'; +import { handleTemplate } from '@/lib/operations.utils'; +import { LaboratoryPluginsActions, LaboratoryPluginsState } from '@/lib/plugins'; // import z from 'zod'; import { asyncInterval } from '@/lib/utils'; import { SubscriptionProtocol, UrlLoader } from '@graphql-tools/url-loader'; @@ -24,11 +29,16 @@ export interface LaboratoryEndpointActions { restoreDefaultEndpoint: () => void; } +export const EXPECTED_ERROR_REASON = 'Expected error reason'; + export const useEndpoint = (props: { defaultEndpoint?: string | null; onEndpointChange?: (endpoint: string | null) => void; defaultSchemaIntrospection?: IntrospectionQuery | null; settingsApi?: LaboratorySettingsState & LaboratorySettingsActions; + operationsApi?: LaboratoryOperationsState & LaboratoryOperationsActions; + envApi?: LaboratoryEnvState & LaboratoryEnvActions; + pluginsApi?: LaboratoryPluginsState & LaboratoryPluginsActions; }): LaboratoryEndpointState & LaboratoryEndpointActions => { const [endpoint, _setEndpoint] = useState(props.defaultEndpoint ?? null); const [introspection, setIntrospection] = useState(null); @@ -47,72 +57,99 @@ export const useEndpoint = (props: { const loader = useMemo(() => new UrlLoader(), []); - const fetchSchema = useCallback( - async (signal?: AbortSignal) => { - if (endpoint === props.defaultEndpoint && props.defaultSchemaIntrospection) { - setIntrospection(props.defaultSchemaIntrospection); - return; - } - - if (!endpoint) { - setIntrospection(null); - return; - } - - try { - 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, - }), - }); - - if (result.length === 0) { - throw new Error('Failed to fetch schema'); - } - - if (!result[0].schema) { - throw new Error('Failed to fetch schema'); - } - - setIntrospection(introspectionFromSchema(result[0].schema)); - } catch (error: unknown) { - if ( - error && - typeof error === 'object' && - 'message' in error && - typeof error.message === 'string' - ) { - toast.error(error.message); - } else { - toast.error('Failed to fetch schema'); - } - - setIntrospection(null); - - throw error; - } - }, + const fetchSchema = useMemo( + () => + throttle( + async ( + signal?: AbortSignal, + options?: { + env?: LaboratoryEnv; + pluginsState?: Record; + }, + ) => { + if (endpoint === props.defaultEndpoint && props.defaultSchemaIntrospection) { + setIntrospection(props.defaultSchemaIntrospection); + return; + } + + if (!endpoint) { + setIntrospection(null); + return; + } + + try { + const parsedHeaders = props.operationsApi?.activeOperation?.headers + ? JSON.parse( + handleTemplate(props.operationsApi?.activeOperation?.headers, { + ...(options?.env?.variables ?? {}), + plugins: options?.pluginsState ?? {}, + }), + ) + : {}; + + const result = await loader.load(endpoint, { + subscriptionsEndpoint: endpoint, + subscriptionsProtocol: + (props.settingsApi?.settings.subscriptions.protocol as SubscriptionProtocol) ?? + SubscriptionProtocol.GRAPHQL_SSE, + headers: parsedHeaders, + 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, + }), + }); + + if (result.length === 0) { + throw new Error('Failed to fetch schema'); + } + + if (!result[0].schema) { + throw new Error('Failed to fetch schema'); + } + + setIntrospection(introspectionFromSchema(result[0].schema)); + } catch (error: unknown) { + if ( + error && + typeof error === 'object' && + 'message' in error && + typeof error.message === 'string' + ) { + if (error.message === EXPECTED_ERROR_REASON) { + return; + } + + toast.error(error.message); + } else { + toast.error('Failed to fetch schema'); + } + + setIntrospection(null); + + throw error; + } + }, + 500, + ), [ endpoint, props.settingsApi?.settings.fetch.timeout, props.settingsApi?.settings.introspection.method, props.settingsApi?.settings.introspection.schemaDescription, + props.operationsApi?.activeOperation?.headers, + props.envApi?.env?.variables, + props.pluginsApi?.pluginsState, ], ); @@ -132,7 +169,7 @@ export const useEndpoint = (props: { try { await fetchSchema(intervalController.signal); } catch { - intervalController.abort(); + intervalController.abort(new Error('Aborted because of schema polling error')); } }, 5000, @@ -140,7 +177,7 @@ export const useEndpoint = (props: { ); return () => { - intervalController.abort(); + intervalController.abort(new Error(EXPECTED_ERROR_REASON)); }; }, [shouldPollSchema, fetchSchema]); @@ -152,7 +189,13 @@ export const useEndpoint = (props: { useEffect(() => { if (endpoint && !shouldPollSchema) { - void fetchSchema(); + const abortController = new AbortController(); + + void fetchSchema(abortController.signal); + + return () => { + abortController.abort(new Error(EXPECTED_ERROR_REASON)); + }; } }, [endpoint, fetchSchema, shouldPollSchema]); From 53fb6318a69aeb6a5b7da7328d7c8bf5e6280a9d Mon Sep 17 00:00:00 2001 From: Mykhailo Skorokhodov Date: Thu, 7 May 2026 11:22:35 +0200 Subject: [PATCH 2/4] fix: resolved deps to properly debounce schema fetch --- .../src/components/laboratory/settings.tsx | 1 - .../libraries/laboratory/src/lib/endpoint.ts | 73 +++++++++++++++---- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/packages/libraries/laboratory/src/components/laboratory/settings.tsx b/packages/libraries/laboratory/src/components/laboratory/settings.tsx index a335481692..92f6a9802f 100644 --- a/packages/libraries/laboratory/src/components/laboratory/settings.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/settings.tsx @@ -20,7 +20,6 @@ const settingsFormSchema = z.object({ introspection: z.object({ method: z.enum(['GET', 'POST']).optional(), schemaDescription: z.boolean().optional(), - headers: z.string().optional(), }), }); diff --git a/packages/libraries/laboratory/src/lib/endpoint.ts b/packages/libraries/laboratory/src/lib/endpoint.ts index 25ceb64037..f9ff88c7f0 100644 --- a/packages/libraries/laboratory/src/lib/endpoint.ts +++ b/packages/libraries/laboratory/src/lib/endpoint.ts @@ -1,17 +1,16 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { buildClientSchema, GraphQLSchema, introspectionFromSchema, type IntrospectionQuery, } from 'graphql'; -import { throttle } from 'lodash'; +import { debounce } from 'lodash'; import { toast } from 'sonner'; import { LaboratoryEnv, LaboratoryEnvActions, LaboratoryEnvState } from '@/lib/env'; import { LaboratoryOperationsActions, LaboratoryOperationsState } from '@/lib/operations'; import { handleTemplate } from '@/lib/operations.utils'; import { LaboratoryPluginsActions, LaboratoryPluginsState } from '@/lib/plugins'; -// import z from 'zod'; import { asyncInterval } from '@/lib/utils'; import { SubscriptionProtocol, UrlLoader } from '@graphql-tools/url-loader'; import type { LaboratorySettingsActions, LaboratorySettingsState } from './settings'; @@ -57,9 +56,21 @@ export const useEndpoint = (props: { const loader = useMemo(() => new UrlLoader(), []); + const activeOperationHeadersRef = useRef( + props.operationsApi?.activeOperation?.headers, + ); + const envVariablesRef = useRef( + props.envApi?.env?.variables, + ); + const pluginsStateRef = useRef | undefined>(props.pluginsApi?.pluginsState); + + activeOperationHeadersRef.current = props.operationsApi?.activeOperation?.headers; + envVariablesRef.current = props.envApi?.env?.variables; + pluginsStateRef.current = props.pluginsApi?.pluginsState; + const fetchSchema = useMemo( () => - throttle( + debounce( async ( signal?: AbortSignal, options?: { @@ -78,14 +89,20 @@ export const useEndpoint = (props: { } try { - const parsedHeaders = props.operationsApi?.activeOperation?.headers - ? JSON.parse( - handleTemplate(props.operationsApi?.activeOperation?.headers, { - ...(options?.env?.variables ?? {}), - plugins: options?.pluginsState ?? {}, - }), - ) - : {}; + let parsedHeaders: Record = {}; + + try { + parsedHeaders = activeOperationHeadersRef.current + ? JSON.parse( + handleTemplate(activeOperationHeadersRef.current, { + ...(options?.env?.variables ?? envVariablesRef.current ?? {}), + plugins: options?.pluginsState ?? pluginsStateRef.current ?? {}, + }), + ) + : {}; + } catch (error: unknown) { + toast.error('Failed to parse headers'); + } const result = await loader.load(endpoint, { subscriptionsEndpoint: endpoint, @@ -147,12 +164,15 @@ export const useEndpoint = (props: { props.settingsApi?.settings.fetch.timeout, props.settingsApi?.settings.introspection.method, props.settingsApi?.settings.introspection.schemaDescription, - props.operationsApi?.activeOperation?.headers, - props.envApi?.env?.variables, - props.pluginsApi?.pluginsState, ], ); + useEffect(() => { + return () => { + fetchSchema.cancel(); + }; + }, [fetchSchema]); + const shouldPollSchema = useMemo(() => { return endpoint !== props.defaultEndpoint || !props.defaultSchemaIntrospection; }, [endpoint, props.defaultEndpoint, props.defaultSchemaIntrospection]); @@ -199,6 +219,29 @@ export const useEndpoint = (props: { } }, [endpoint, fetchSchema, shouldPollSchema]); + useEffect(() => { + if (!endpoint || !shouldPollSchema) { + return; + } + + const abortController = new AbortController(); + void fetchSchema(abortController.signal, { + env: props.envApi?.env ?? undefined, + pluginsState: props.pluginsApi?.pluginsState, + }); + + return () => { + abortController.abort(new Error(EXPECTED_ERROR_REASON)); + }; + }, [ + endpoint, + shouldPollSchema, + fetchSchema, + props.operationsApi?.activeOperation?.headers, + props.envApi?.env?.variables, + props.pluginsApi?.pluginsState, + ]); + return { endpoint, setEndpoint, From 195b82ac9bf727e9cecf25ba5c714b278d9e8f7d Mon Sep 17 00:00:00 2001 From: Mykhailo Skorokhodov Date: Sat, 9 May 2026 00:19:55 +0200 Subject: [PATCH 3/4] added changeset --- .changeset/ripe-loops-drum.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/ripe-loops-drum.md diff --git a/.changeset/ripe-loops-drum.md b/.changeset/ripe-loops-drum.md new file mode 100644 index 0000000000..9bca28d131 --- /dev/null +++ b/.changeset/ripe-loops-drum.md @@ -0,0 +1,6 @@ +--- +'@graphql-hive/laboratory': patch +'@graphql-hive/render-laboratory': patch +--- + +Hive laboratory introspection query to include active tab headers From c7f3c6f97c2c0007b7b4ff5de4a7b8b27803432d Mon Sep 17 00:00:00 2001 From: Mykhailo Skorokhodov Date: Tue, 12 May 2026 23:32:09 +0200 Subject: [PATCH 4/4] added: settings to manage global introspection query and toggle to include active operation headers --- .../src/components/laboratory/builder.tsx | 67 ++++++++++++----- .../src/components/laboratory/editor.tsx | 7 +- .../src/components/laboratory/laboratory.tsx | 1 + .../src/components/laboratory/settings.tsx | 59 ++++++++++++--- packages/libraries/laboratory/src/index.css | 4 + .../libraries/laboratory/src/lib/endpoint.ts | 75 ++++++++++++++++--- .../libraries/laboratory/src/lib/settings.ts | 8 ++ 7 files changed, 180 insertions(+), 41 deletions(-) diff --git a/packages/libraries/laboratory/src/components/laboratory/builder.tsx b/packages/libraries/laboratory/src/components/laboratory/builder.tsx index 395802c17c..129f615c3b 100644 --- a/packages/libraries/laboratory/src/components/laboratory/builder.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/builder.tsx @@ -19,6 +19,7 @@ import { ListTreeIcon, RotateCcwIcon, SearchIcon, + SettingsIcon, TextAlignStartIcon, } from 'lucide-react'; import { toast } from 'sonner'; @@ -759,7 +760,17 @@ export const Builder = (props: { operationName?: string | null; isReadOnly?: boolean; }) => { - const { schema, activeOperation, endpoint, setEndpoint, defaultEndpoint } = useLaboratory(); + const { + schema, + activeOperation, + endpoint, + setEndpoint, + defaultEndpoint, + tabs, + addTab, + setActiveTab, + shouldPollSchema, + } = useLaboratory(); const [endpointValue, setEndpointValue] = useState(endpoint ?? ''); const [searchValue, setSearchValue] = useState(''); @@ -845,23 +856,45 @@ export const Builder = (props: { return (
-
+
Builder -
- - - - - Collapse all - +
+ {shouldPollSchema && ( + + )} +
+ + + + + Collapse all + +
diff --git a/packages/libraries/laboratory/src/components/laboratory/editor.tsx b/packages/libraries/laboratory/src/components/laboratory/editor.tsx index 1944fa47a8..19a55ef972 100644 --- a/packages/libraries/laboratory/src/components/laboratory/editor.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/editor.tsx @@ -12,6 +12,7 @@ import { OperationDefinitionNode, parse } from 'graphql'; import * as monaco from 'monaco-editor'; import { MonacoGraphQLAPI } from 'monaco-graphql/esm/api.js'; import { initializeMode } from 'monaco-graphql/initializeMode'; +import { cn } from '@/lib/utils'; import MonacoEditor, { loader } from '@monaco-editor/react'; import { useLaboratory } from './context'; @@ -84,7 +85,7 @@ const darkTheme: monaco.editor.IStandaloneThemeData = { ], colors: { 'editor.foreground': '#f6f8fa', - 'editor.background': '#0f1214', + 'editor.background': '#0f121400', 'editor.selectionBackground': '#2A2F34', 'editor.inactiveSelectionBackground': '#2A2F34', 'editor.lineHighlightBackground': '#2A2F34', @@ -354,10 +355,10 @@ const EditorInner = forwardRef((props, ref) => { } return ( -
+
{ validators: { onSubmit: settingsFormSchema, }, - onSubmit: ({ value }) => { - setSettings(value as typeof settings); - }, }); + useEffect(() => { + form.store.subscribe(state => { + setSettings(state.currentVal.values); + }); + }, [setSettings]); + return (
-
+ Fetch @@ -220,6 +222,43 @@ export const Settings = () => { ); }} + + {field => { + return ( + + Headers + + + ); + }} + + + {field => { + return ( + + +
+ + Include active operation headers + + + Active operation (tab) headers will be included in the introspection query + +
+
+ ); + }} +
diff --git a/packages/libraries/laboratory/src/index.css b/packages/libraries/laboratory/src/index.css index 7bf2bdff06..e9f44c44df 100644 --- a/packages/libraries/laboratory/src/index.css +++ b/packages/libraries/laboratory/src/index.css @@ -93,6 +93,10 @@ --destructive-foreground: var(--hive-laboratory-destructive-foreground, var(--color-neutral-1)); --ring: var(--hive-laboratory-ring, var(--color-ring)); + + & .monaco-editor { + --vscode-focusBorder: transparent !important; + } } .hive-laboratory.dark { diff --git a/packages/libraries/laboratory/src/lib/endpoint.ts b/packages/libraries/laboratory/src/lib/endpoint.ts index f9ff88c7f0..8281c072eb 100644 --- a/packages/libraries/laboratory/src/lib/endpoint.ts +++ b/packages/libraries/laboratory/src/lib/endpoint.ts @@ -11,6 +11,7 @@ import { LaboratoryEnv, LaboratoryEnvActions, LaboratoryEnvState } from '@/lib/e import { LaboratoryOperationsActions, LaboratoryOperationsState } from '@/lib/operations'; import { handleTemplate } from '@/lib/operations.utils'; import { LaboratoryPluginsActions, LaboratoryPluginsState } from '@/lib/plugins'; +import { LaboratoryPreflightActions, LaboratoryPreflightState } from '@/lib/preflight'; import { asyncInterval } from '@/lib/utils'; import { SubscriptionProtocol, UrlLoader } from '@graphql-tools/url-loader'; import type { LaboratorySettingsActions, LaboratorySettingsState } from './settings'; @@ -20,6 +21,7 @@ export interface LaboratoryEndpointState { schema: GraphQLSchema | null; introspection: IntrospectionQuery | null; defaultEndpoint: string | null; + shouldPollSchema: boolean; } export interface LaboratoryEndpointActions { @@ -38,6 +40,7 @@ export const useEndpoint = (props: { operationsApi?: LaboratoryOperationsState & LaboratoryOperationsActions; envApi?: LaboratoryEnvState & LaboratoryEnvActions; pluginsApi?: LaboratoryPluginsState & LaboratoryPluginsActions; + preflightApi?: LaboratoryPreflightState & LaboratoryPreflightActions; }): LaboratoryEndpointState & LaboratoryEndpointActions => { const [endpoint, _setEndpoint] = useState(props.defaultEndpoint ?? null); const [introspection, setIntrospection] = useState(null); @@ -89,19 +92,67 @@ export const useEndpoint = (props: { } try { + let env = options?.env?.variables ?? envVariablesRef.current ?? {}; + let plugins = options?.pluginsState ?? pluginsStateRef.current ?? {}; + + let sourceHeaders: Record = {}; + + if (props.settingsApi?.settings.introspection.headers) { + try { + sourceHeaders = JSON.parse(props.settingsApi?.settings.introspection.headers); + } catch {} + } + + if ( + props.settingsApi?.settings.introspection.includeActiveOperationHeaders && + activeOperationHeadersRef.current + ) { + try { + sourceHeaders = { + ...sourceHeaders, + ...JSON.parse(activeOperationHeadersRef.current), + }; + } catch {} + } + + let stringifiedHeaders = JSON.stringify(sourceHeaders); + + if (stringifiedHeaders.includes('{{')) { + try { + const preflightResult = await props.preflightApi?.runPreflight?.( + props.pluginsApi?.plugins ?? [], + props.pluginsApi?.pluginsState ?? {}, + ); + + props?.envApi?.setEnv(preflightResult?.env ?? { variables: {} }); + props?.pluginsApi?.setPluginsState(preflightResult?.pluginsState ?? {}); + + env = preflightResult?.env?.variables ?? {}; + plugins = preflightResult?.pluginsState ?? {}; + + if (preflightResult?.headers) { + stringifiedHeaders = JSON.stringify({ + ...sourceHeaders, + ...preflightResult?.headers, + }); + } + } catch (error: unknown) { + toast.error('Failed to run preflight'); + } + } + let parsedHeaders: Record = {}; try { - parsedHeaders = activeOperationHeadersRef.current - ? JSON.parse( - handleTemplate(activeOperationHeadersRef.current, { - ...(options?.env?.variables ?? envVariablesRef.current ?? {}), - plugins: options?.pluginsState ?? pluginsStateRef.current ?? {}, - }), - ) - : {}; + parsedHeaders = JSON.parse( + handleTemplate(stringifiedHeaders, { + ...env, + plugins, + }), + ); } catch (error: unknown) { toast.error('Failed to parse headers'); + parsedHeaders = {}; } const result = await loader.load(endpoint, { @@ -164,6 +215,8 @@ export const useEndpoint = (props: { props.settingsApi?.settings.fetch.timeout, props.settingsApi?.settings.introspection.method, props.settingsApi?.settings.introspection.schemaDescription, + props.settingsApi?.settings.introspection.headers, + props.settingsApi?.settings.introspection.includeActiveOperationHeaders, ], ); @@ -237,9 +290,8 @@ export const useEndpoint = (props: { endpoint, shouldPollSchema, fetchSchema, - props.operationsApi?.activeOperation?.headers, - props.envApi?.env?.variables, - props.pluginsApi?.pluginsState, + props.settingsApi?.settings.introspection.headers, + props.settingsApi?.settings.introspection.includeActiveOperationHeaders, ]); return { @@ -250,5 +302,6 @@ export const useEndpoint = (props: { fetchSchema, restoreDefaultEndpoint, defaultEndpoint: props.defaultEndpoint ?? null, + shouldPollSchema, }; }; diff --git a/packages/libraries/laboratory/src/lib/settings.ts b/packages/libraries/laboratory/src/lib/settings.ts index 20edcfe701..25df85ae7f 100644 --- a/packages/libraries/laboratory/src/lib/settings.ts +++ b/packages/libraries/laboratory/src/lib/settings.ts @@ -13,6 +13,8 @@ export type LaboratorySettings = { introspection: { method?: 'GET' | 'POST'; schemaDescription?: boolean; + headers?: string; + includeActiveOperationHeaders?: boolean; }; }; @@ -29,6 +31,8 @@ export const defaultLaboratorySettings: LaboratorySettings = { introspection: { method: 'POST', schemaDescription: false, + headers: '', + includeActiveOperationHeaders: false, }, }; @@ -50,6 +54,10 @@ export const normalizeLaboratorySettings = ( schemaDescription: settings?.introspection?.schemaDescription ?? defaultLaboratorySettings.introspection.schemaDescription, + headers: settings?.introspection?.headers ?? defaultLaboratorySettings.introspection.headers, + includeActiveOperationHeaders: + settings?.introspection?.includeActiveOperationHeaders ?? + defaultLaboratorySettings.introspection.includeActiveOperationHeaders, }, });