UNPKG

graphql

Version:

A Query Language and Runtime which can target any service.

308 lines (307 loc) 14 kB
/** * TracingChannel integration. * * GraphQL.js publishes lifecycle events on a set of named tracing channels * that application performance monitoring (APM) tools can subscribe to in * order to observe parse, validate, execute, subscribe, and resolver behavior, * plus selected executor internals. At module load time GraphQL.js resolves * `node:diagnostics_channel` itself so APMs do not need to interact with the * GraphQL API to enable tracing. On runtimes that do not expose * `node:diagnostics_channel` (e.g., browsers) the load silently no-ops and * emission sites short-circuit. * * Within the tracing context types, `error` means the traced JavaScript call * threw or rejected; it does not mean every `GraphQLError` returned by * GraphQL.js. Some channels complete normally and publish GraphQL errors on * `result`. Resolver errors can appear both as `message.error` on * `graphql:resolve` and as formatted errors in an enclosing execution or * subscription result. `graphql:parse`, `graphql:validate`, and * `graphql:execute:variableCoercion` are sync-only channels. * @category Diagnostics */ import type { Maybe } from "./jsutils/Maybe.mjs"; import type { ObjMap } from "./jsutils/ObjMap.mjs"; import type { GraphQLError } from "./error/GraphQLError.mjs"; import type { DocumentNode, OperationDefinitionNode, OperationTypeNode } from "./language/ast.mjs"; import type { Source } from "./language/source.mjs"; import type { GraphQLSchema } from "./type/schema.mjs"; import type { ExecutionResult } from "./execution/Executor.mjs"; import type { ExperimentalIncrementalExecutionResults } from "./execution/incremental/IncrementalExecutor.mjs"; import type { VariableValues } from "./execution/values.mjs"; /** * Structural subset of `DiagnosticsChannel` sufficient for publishing and * subscriber gating. The `node:diagnostics_channel` `Channel` satisfies this. * * @internal */ export interface MinimalChannel<TMessage = unknown> { readonly hasSubscribers?: boolean; publish: (message: TMessage) => void; runStores: <T, ContextType extends object>(context: ContextType, fn: (this: ContextType, ...args: Array<unknown>) => T, thisArg?: unknown, ...args: Array<unknown>) => T; } /** * Structural subset of the Node.js `TracingChannel` API. The * `node:diagnostics_channel` `TracingChannel` satisfies this by duck typing, * so GraphQL.js does not need a dependency on `@types/node` or on the runtime * itself. * * @internal */ export interface MinimalTracingChannel<TContext = unknown> { readonly hasSubscribers: boolean | undefined; readonly start: MinimalChannel<TContext>; readonly end: MinimalChannel<TContext>; readonly asyncStart: MinimalChannel<TContext>; readonly asyncEnd: MinimalChannel<TContext>; readonly error: MinimalChannel<TContext>; traceSync: <T>(fn: (...args: Array<unknown>) => T, context: TContext extends object ? TContext : object, thisArg?: unknown, ...args: Array<unknown>) => T; } /** Context published on the sync-only `graphql:parse` channel. */ export interface GraphQLParseContext { /** Source text or source object passed to the parser. */ source: string | Source; /** Error thrown while parsing, when parsing fails. */ error?: unknown; /** Parsed document, when parsing succeeds. */ result?: DocumentNode; } /** Context published on the sync-only `graphql:validate` channel. */ export interface GraphQLValidateContext { /** Schema used for validation. */ schema: GraphQLSchema; /** Parsed document being validated. */ document: DocumentNode; /** Error thrown while validating, when validation fails abruptly. */ error?: unknown; /** Validation errors returned by validation. */ result?: ReadonlyArray<GraphQLError>; } /** * Context published on `graphql:execute`. * * Returned results may contain GraphQL errors collected during execution. */ export interface GraphQLExecuteContext { /** Schema used for execution. */ schema: GraphQLSchema; /** Parsed document being executed. */ document: DocumentNode; /** Raw variable values provided by the caller before coercion. */ rawVariableValues: Maybe<{ readonly [variable: string]: unknown; }>; /** Selected operation name, if one is available. */ operationName: string | undefined; /** Selected operation type, if one is available. */ operationType: OperationTypeNode | undefined; /** Error thrown or rejected while executing, when execution fails abruptly. */ error?: unknown; /** Execution result returned by execution, including GraphQL errors. */ result?: ExecutionResult | ExperimentalIncrementalExecutionResults; } /** * Context published on `graphql:execute:rootSelectionSet`. * * Returned results may contain GraphQL errors collected during execution. */ export interface GraphQLExecuteRootSelectionSetContext { /** Schema used for execution. */ schema: GraphQLSchema; /** Parsed document being executed. */ document: DocumentNode; /** Operation definition selected for execution. */ operation: OperationDefinitionNode; /** Raw variable values provided by the caller before coercion. */ rawVariableValues: Maybe<{ readonly [variable: string]: unknown; }>; /** Selected operation name, if one is available. */ operationName: string | undefined; /** Selected operation type. */ operationType: OperationTypeNode; /** Error thrown or rejected while executing the root selection set. */ error?: unknown; /** * Execution result returned from the root selection set, including GraphQL * errors. */ result?: ExecutionResult | ExperimentalIncrementalExecutionResults; } /** * Context published on `graphql:execute:variableCoercion`. * * Coercion runs synchronously while execution arguments are validated, so only * the `start`/`end` (and, on an abrupt throw, `error`) lifecycle fires. * Ordinary variable coercion failures are returned on `result.errors`; when * execution is invoked through APIs such as `execute()` or `subscribe()`, they * surface as GraphQL result errors rather than as the tracing `error` * lifecycle event. */ export interface GraphQLExecuteVariableCoercionContext { /** Schema used for variable coercion. */ schema: GraphQLSchema; /** Parsed document being executed. */ document: DocumentNode; /** Operation definition whose variables are being coerced. */ operation: OperationDefinitionNode; /** Raw variable values provided by the caller before coercion. */ rawVariableValues: Maybe<{ readonly [variable: string]: unknown; }>; /** Selected operation name, if one is available. */ operationName: string | undefined; /** Selected operation type. */ operationType: OperationTypeNode; /** Error thrown while coercing variables, when coercion fails abruptly. */ error?: unknown; /** Coerced variable values or coercion errors returned by coercion. */ result?: { variableValues: VariableValues; } | { errors: ReadonlyArray<GraphQLError>; }; } /** * Context published on `graphql:subscribe`. * * Subscription source resolver errors and invalid source stream results are * returned on `result` as ExecutionResult errors; they do not publish the * `error` lifecycle event unless subscription setup fails abruptly before * GraphQL can form a result. */ export interface GraphQLSubscribeContext { /** Schema used for subscription execution. */ schema: GraphQLSchema; /** Parsed subscription document. */ document: DocumentNode; /** Raw variable values provided by the caller before coercion. */ rawVariableValues: Maybe<{ readonly [variable: string]: unknown; }>; /** Selected operation name, if one is available. */ operationName: string | undefined; /** Selected operation type, if one is available. */ operationType: OperationTypeNode | undefined; /** Error thrown or rejected while subscribing, when setup fails abruptly. */ error?: unknown; /** * Subscription response stream, or an ExecutionResult containing GraphQL * errors. */ result?: AsyncGenerator<ExecutionResult, void, void> | ExecutionResult; } /** * Context published on `graphql:resolve`. * * Resolver throws and rejections publish the `error` lifecycle event here. * The same failure may also be formatted into the enclosing execution or * subscription result. */ export interface GraphQLResolveContext { /** Field name being resolved. */ fieldName: string; /** Response alias for the field being resolved. */ alias: string; /** Parent type name for the field being resolved. */ parentType: string; /** Return type string for the field being resolved. */ fieldType: string; /** Argument values passed to the resolver. */ args: ObjMap<unknown>; /** Whether the field is using the default resolver. */ isDefaultResolver: boolean; /** Response path for the field being resolved. */ fieldPath: string; /** Error thrown or rejected by the resolver, when resolution fails. */ error?: unknown; /** Value returned by the resolver, when resolution succeeds. */ result?: unknown; } /** Mapping from tracing channel name to the context type published on it. */ export interface GraphQLChannelContextByName { /** Context published on `graphql:parse`. */ 'graphql:parse': GraphQLParseContext; /** Context published on `graphql:validate`. */ 'graphql:validate': GraphQLValidateContext; /** Context published on `graphql:execute`. */ 'graphql:execute': GraphQLExecuteContext; /** Context published on `graphql:execute:variableCoercion`. */ 'graphql:execute:variableCoercion': GraphQLExecuteVariableCoercionContext; /** Context published on `graphql:execute:rootSelectionSet`. */ 'graphql:execute:rootSelectionSet': GraphQLExecuteRootSelectionSetContext; /** Context published on `graphql:subscribe`. */ 'graphql:subscribe': GraphQLSubscribeContext; /** Context published on `graphql:resolve`. */ 'graphql:resolve': GraphQLResolveContext; } /** * The collection of tracing channels GraphQL.js emits on. Application * performance monitoring (APM) tools subscribe to these by name on their own * `node:diagnostics_channel` import; both paths land on the same channel * instance because `tracingChannel(name)` is cached by name. */ export interface GraphQLChannels { /** Tracing channel for `graphql:execute`. */ execute: MinimalTracingChannel<GraphQLExecuteContext>; /** Tracing channel for `graphql:execute:variableCoercion`. */ executeVariableCoercion: MinimalTracingChannel<GraphQLExecuteVariableCoercionContext>; /** Tracing channel for `graphql:execute:rootSelectionSet`. */ executeRootSelectionSet: MinimalTracingChannel<GraphQLExecuteRootSelectionSetContext>; /** Tracing channel for `graphql:parse`. */ parse: MinimalTracingChannel<GraphQLParseContext>; /** Tracing channel for `graphql:validate`. */ validate: MinimalTracingChannel<GraphQLValidateContext>; /** Tracing channel for `graphql:resolve`. */ resolve: MinimalTracingChannel<GraphQLResolveContext>; /** Tracing channel for `graphql:subscribe`. */ subscribe: MinimalTracingChannel<GraphQLSubscribeContext>; } /** * Per-channel handles, resolved once at module load. `undefined` when * `node:diagnostics_channel` isn't available. Emission sites read these * directly to keep the no-subscriber fast path to a single property access * plus a `hasSubscribers` check (no function calls, no closures). * * @internal */ export declare const parseChannel: MinimalTracingChannel<GraphQLParseContext> | undefined; /** @internal */ export declare const validateChannel: MinimalTracingChannel<GraphQLValidateContext> | undefined; /** @internal */ export declare const executeChannel: MinimalTracingChannel<GraphQLExecuteContext> | undefined; /** @internal */ export declare const executeVariableCoercionChannel: MinimalTracingChannel<GraphQLExecuteVariableCoercionContext> | undefined; /** @internal */ export declare const executeRootSelectionSetChannel: MinimalTracingChannel<GraphQLExecuteRootSelectionSetContext> | undefined; /** @internal */ export declare const subscribeChannel: MinimalTracingChannel<GraphQLSubscribeContext> | undefined; /** @internal */ export declare const resolveChannel: MinimalTracingChannel<GraphQLResolveContext> | undefined; /** * Whether emission sites should publish to `channel`. Trusts the * `TracingChannel.hasSubscribers` aggregate when the runtime exposes it; if * the getter is missing (e.g. Bun's `node:diagnostics_channel`, where * `tracingChannel.hasSubscribers` is `undefined`), falls back to checking * each of the five underlying lifecycle channels so a subscriber attached * via `tracingChannel.subscribe(handlers)` is still observed. * * @internal */ export declare function shouldTrace<TContext = unknown>(channel: MinimalTracingChannel<TContext> | undefined): channel is MinimalTracingChannel<TContext>; interface TraceLifecycleContext { error?: unknown; result?: unknown; } type TraceStartContext<TContext extends TraceLifecycleContext> = Omit<TContext, 'error' | 'result'>; /** * Publish a traced call that may complete synchronously or with a promise. * Caller has already verified that a subscriber is attached. On normal * completion, `result` is attached before the terminal `end` or `asyncEnd` * event. When the traced call throws or rejects, `error` is attached, the * `error` sub-channel fires, and the terminal `end` or `asyncEnd` event is * published before the original failure is propagated. * * @internal */ export declare function traceMixed<TResult, TContext extends TraceLifecycleContext>(channel: MinimalTracingChannel<TContext>, contextInput: TraceStartContext<TContext>, fn: () => TResult): TResult; export {};