@znight/graphql 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import { Token } from "@znight/core";
2
- import { GuardEnforcement, HttpContext, RouteMetaCarrier } from "@znight/http";
3
- import { ConstValueNode, DocumentNode, GraphQLResolveInfo } from "graphql";
1
+ import { Container, CoreLogger, Token } from "@znight/core";
2
+ import { ByteSize, GuardEnforcement, HttpContext, HttpPlugin, PluginRoute, RouteMetaCarrier } from "@znight/http";
3
+ import { ConstValueNode, DocumentNode, ExecutionArgs, ExecutionResult, FormattedExecutionResult, GraphQLError, GraphQLFormattedError, GraphQLResolveInfo, GraphQLSchema, Source, ValidationRule } from "graphql";
4
4
  /**
5
5
  * GraphQL errors shown to clients (RFC 0005 §5.10, §8.8): `GraphQLException` and the
6
6
  * `extensions.code` values the plugin produces (Apollo conventions, which clients already know).
@@ -70,8 +70,11 @@ declare class GraphQLException extends Error {
70
70
  type GraphQLArgs = Readonly<Record<string, unknown>>;
71
71
  /** Kind of a GraphQL operation. */
72
72
  type GraphQLOperationKind = "query" | "mutation" | "subscription";
73
- /** How an operation reached the server: GraphQL over HTTP, GraphQL over SSE, or `plugin.execute()`. */
74
- type GraphQLTransport = "http" | "sse" | "direct";
73
+ /**
74
+ * How an operation reached the server: GraphQL over HTTP, GraphQL over SSE, GraphQL over WebSocket
75
+ * (`ws`, reserved for `graphql-ws`), or `plugin.execute()` / `plugin.subscribe()` (`direct`).
76
+ */
77
+ type GraphQLTransport = "http" | "sse" | "ws" | "direct";
75
78
  /**
76
79
  * Options of a loader (§5.5), read once per loader class: the `options` property of a
77
80
  * {@link BatchLoad} provider. They are DataLoader's, except `batchScheduleFn` (Znight batches per
@@ -160,6 +163,13 @@ interface GraphQLContext {
160
163
  readonly signal: AbortSignal;
161
164
  /** Per-operation storage. */
162
165
  readonly state: Map<string | symbol, unknown>;
166
+ /**
167
+ * The context of the GraphQL server that received the operation through a driver (RFC 0009): the
168
+ * `contextValue` it passed to `GraphQLEngine.execute()` (Yoga's initial context, Apollo's context
169
+ * value). `undefined` for the built-in transport and direct calls. Resolvers do not need it: the
170
+ * request is `http`, the user a request-scoped provider.
171
+ */
172
+ readonly serverContext: unknown;
163
173
  /** The DataLoader of the current execution for a loader class (`ctx.loader(AuthorsById)`). */
164
174
  loader<L extends BatchLoad<unknown, unknown>>(loader: BatchLoadClass<L>): BatchLoader<LoaderKey<L>, LoaderValue<L>>;
165
175
  /**
@@ -168,9 +178,16 @@ interface GraphQLContext {
168
178
  */
169
179
  loader(token: Token): BatchLoader<unknown, unknown>;
170
180
  }
181
+ declare module "@znight/core" {
182
+ /** Custom parameter decorators (`createParamDecorator`) resolve GraphQL parameters from the {@link ResolverContext}. */
183
+ interface ParamContexts {
184
+ graphql: ResolverContext;
185
+ }
186
+ }
171
187
  /**
172
- * What a {@link GraphQLGuard} receives for one resolver call (§5.9). It satisfies the
173
- * `RouteMetaCarrier` of `@znight/http`, so route decorators read it: `Roles.get(ctx)`.
188
+ * What a {@link GraphQLGuard} receives for one resolver call (§5.9), and the context of custom
189
+ * parameter resolvers (RFC 0010). It satisfies the `RouteMetaCarrier` of `@znight/http`, so route
190
+ * decorators read it: `Roles.get(ctx)`.
174
191
  */
175
192
  interface ResolverContext<TParent = unknown, TArgs extends GraphQLArgs = GraphQLArgs> extends RouteMetaCarrier {
176
193
  /** Effective metadata of the field (method decisions over class decisions) and its schema coordinate. */
@@ -262,5 +279,172 @@ interface ScalarDefinition<T> {
262
279
  /** Literal of the document → internal value. Default: `parseValue` of the literal's JSON value. */
263
280
  parseLiteral?(literal: ConstValueNode): T;
264
281
  }
265
- export { GraphQLExceptionOptions as C, GraphQLException as S, LoaderValue as _, GraphQLContext as a, GraphQLErrorCode as b, GraphQLGuard as c, GraphQLResultMapper as d, GraphQLResultMapperClass as f, LoaderOptions as g, LoaderKey as h, GraphQLArgs as i, GraphQLGuardClass as l, GraphQLTransport as m, BatchLoadClass as n, GraphQLErrorMapper as o, GraphQLResultTypes as p, BatchLoader as r, GraphQLErrorMapperClass as s, BatchLoad as t, GraphQLOperationKind as u, ResolverContext as v, GraphQLErrorCodeValue as x, ScalarDefinition as y };
266
- //# sourceMappingURL=types-DQ713UgI.d.mts.map
282
+ /** Options of {@link orderByKeys}. */
283
+ interface OrderByKeysOptions {
284
+ /**
285
+ * What a key without value gets: `'error'` (default) a `NOT_FOUND` `GraphQLException` failing that
286
+ * key only, `'null'` the value `null`.
287
+ */
288
+ readonly missing?: "error" | "null";
289
+ }
290
+ /**
291
+ * Reorders `values` (rows in any order, some keys possibly missing) to match `keys`, what a
292
+ * `BatchLoad.load()` must return. Keys are compared with `Map` semantics (`===`, `NaN` equal).
293
+ *
294
+ * ```ts
295
+ * async load(ids: readonly ID[]): Promise<readonly (Author | Error)[]> {
296
+ * return orderByKeys(ids, await this.authors.findMany(ids), author => author.id)
297
+ * }
298
+ * ```
299
+ */
300
+ declare function orderByKeys<K, V>(keys: readonly K[], values: Iterable<V>, keyOf: (value: V) => K, options?: OrderByKeysOptions & {
301
+ readonly missing?: "error";
302
+ }): (V | Error)[];
303
+ declare function orderByKeys<K, V>(keys: readonly K[], values: Iterable<V>, keyOf: (value: V) => K, options: OrderByKeysOptions & {
304
+ readonly missing: "null";
305
+ }): (V | null)[];
306
+ /** A GraphQL request: the body of a GraphQL-over-HTTP request, or the argument of `plugin.execute()`. */
307
+ interface GraphQLRequest {
308
+ readonly query: string;
309
+ readonly operationName?: string | null | undefined;
310
+ readonly variables?: Readonly<Record<string, unknown>> | null | undefined;
311
+ /** Request extensions (ignored by the executor; available to transports). */
312
+ readonly extensions?: Readonly<Record<string, unknown>> | null | undefined;
313
+ }
314
+ /** The results of a subscription (one per event), or the single result of a query sent as a stream. */
315
+ interface GraphQLResultStream extends AsyncIterableIterator<FormattedExecutionResult> {
316
+ /** Ends the stream: the source event stream is returned (unsubscribed). */
317
+ return(): Promise<IteratorResult<FormattedExecutionResult>>;
318
+ }
319
+ /** A value of type `T` or a promise of it (graphql-js `PromiseOrValue`). */
320
+ type PromiseOrValue<T> = T | Promise<T>;
321
+ /** Options of {@link GraphQLEngine.execute} and {@link GraphQLEngine.subscribe}. */
322
+ interface EngineExecuteOptions {
323
+ /**
324
+ * The protocol that carried the operation (`GraphQLOperationEvent.transport`,
325
+ * `GraphQLContext.transport`): `'http'` (default), `'sse'` for an event stream, `'ws'` for
326
+ * `graphql-ws`.
327
+ */
328
+ readonly transport?: "http" | "sse" | "ws";
329
+ /**
330
+ * Cancels the operation (graphql-js `abortSignal`, `GraphQLContext.signal`). Default:
331
+ * `ExecutionArgs.abortSignal`, else the signal of the HTTP request of the current scope.
332
+ */
333
+ readonly signal?: AbortSignal;
334
+ /** `GraphQLContext.requestId`. Default: the request id of the HTTP request of the scope. */
335
+ readonly requestId?: string;
336
+ }
337
+ /** The resolved options a GraphQL server needs to configure itself (read-only). */
338
+ interface GraphQLEngineSettings {
339
+ /** The endpoint path, the `HttpPlugin` prefix included (`/api/graphql`). */
340
+ readonly path: string;
341
+ readonly introspection: boolean;
342
+ /** The `graphiql` option: serve the server's IDE. */
343
+ readonly ide: boolean;
344
+ readonly exposeErrors: boolean;
345
+ /** `limits.maxTokens`, for servers that parse documents themselves. */
346
+ readonly maxTokens: number | undefined;
347
+ readonly subscriptions: false | {
348
+ readonly maxActive: number;
349
+ };
350
+ readonly http: false | {
351
+ readonly get: boolean;
352
+ readonly bodyLimit: ByteSize;
353
+ };
354
+ }
355
+ /**
356
+ * The engine of a bootstrapped `GraphQLPlugin` (`plugin.engine`, and the first argument of
357
+ * `GraphQLDriver.start()`). Its functions are bound: pass them around as they are.
358
+ *
359
+ * ```ts
360
+ * const handler = createHandler({
361
+ * schema: engine.schema,
362
+ * parse: engine.parse,
363
+ * validate: engine.validate,
364
+ * execute: args => engine.execute(args),
365
+ * })
366
+ * ```
367
+ */
368
+ interface GraphQLEngine {
369
+ /** The executable schema: its resolvers are Znight's pipelines. */
370
+ readonly schema: GraphQLSchema;
371
+ readonly settings: GraphQLEngineSettings;
372
+ /**
373
+ * graphql-js `parse` through the document cache: `maxTokens`, then the depth, aliases and
374
+ * directives limits. Throws a `GraphQLError` (`GRAPHQL_PARSE_FAILED`, `QUERY_TOO_COMPLEX`), like
375
+ * graphql-js. A text the engine validated returns the same `DocumentNode`.
376
+ */
377
+ parse(source: string | Source): DocumentNode;
378
+ /**
379
+ * graphql-js `validate` with `rules` (default: graphql-js `specifiedRules`), to which the engine
380
+ * adds its own ({@link rules}), with `hideSuggestions` when introspection is off. With the
381
+ * default rules on {@link schema}, a document is validated once (then cached).
382
+ */
383
+ validate(schema: GraphQLSchema, document: DocumentNode, rules?: readonly ValidationRule[]): readonly GraphQLError[];
384
+ /** The engine's own rules (limits, introspection), for servers that validate documents themselves. */
385
+ readonly rules: readonly ValidationRule[];
386
+ /**
387
+ * Executes a validated query or mutation in the current request scope (or a scope of its own),
388
+ * with the operation's `GraphQLContext` as `contextValue` (`args.contextValue` becomes
389
+ * `GraphQLContext.serverContext`). Errors are mapped and masked, as `GraphQLError` instances.
390
+ */
391
+ execute(args: ExecutionArgs, options?: EngineExecuteOptions): PromiseOrValue<ExecutionResult>;
392
+ /**
393
+ * Starts a validated subscription: a stream of results, or a single error result (rejected, past
394
+ * `subscriptions.maxActive`, a source that could not start). The plugin ends the stream when the
395
+ * application stops.
396
+ */
397
+ subscribe(args: ExecutionArgs, options?: EngineExecuteOptions): PromiseOrValue<AsyncGenerator<ExecutionResult, void, void> | ExecutionResult>;
398
+ }
399
+ /** What {@link GraphQLDriver.routes} receives. */
400
+ interface GraphQLDriverRoutesContext {
401
+ /** The `path` option: the endpoint path, relative to the `HttpPlugin` prefix (`/graphql`). */
402
+ readonly path: string;
403
+ /** The absolute endpoint path, the prefix included (`/api/graphql`). */
404
+ readonly endpoint: string;
405
+ }
406
+ /**
407
+ * A route of a driver: a plugin route of `HttpPlugin` without `meta`, `guards` and `bodyLimit`,
408
+ * which `GraphQLPlugin` adds from its `http` option (`bodyLimit` on the routes that are not `GET`).
409
+ */
410
+ type GraphQLDriverRoute = Pick<PluginRoute, "method" | "path" | "name" | "handler">;
411
+ /** What {@link GraphQLDriver.start} receives besides the engine. */
412
+ interface GraphQLDriverContext {
413
+ /** The application logger. */
414
+ readonly logger: CoreLogger;
415
+ readonly container: Container;
416
+ /** The `HttpPlugin` of the application, when registered (its adapter, for server-mounted drivers). */
417
+ readonly http: HttpPlugin | undefined;
418
+ }
419
+ /**
420
+ * A GraphQL driver, given to `GraphQLPlugin.configure({ driver })`. A route-mounted driver returns
421
+ * its routes, which run in `HttpPlugin`'s pipeline (request scope, hooks, guards, CORS, drain) and
422
+ * call the engine from there; a server-mounted one has no routes and attaches itself to the
423
+ * adapter in `start()`. One driver serves one application at a time.
424
+ */
425
+ interface GraphQLDriver {
426
+ /**
427
+ * The driver's name (`yoga`, `apollo`...): `GraphQLOperationEvent.driver`, logs and errors.
428
+ * `builtin` is reserved for the built-in transport.
429
+ */
430
+ readonly name: string;
431
+ /**
432
+ * The routes `HttpPlugin` serves for the driver, with the `http` option's meta, guards and body
433
+ * limit applied by the plugin. Called once per application, possibly before {@link start}: the
434
+ * handlers MUST NOT use the engine before the first request. Not called with `http: false`.
435
+ */
436
+ routes?(context: GraphQLDriverRoutesContext): readonly GraphQLDriverRoute[];
437
+ /**
438
+ * Called in `GraphQLPlugin.onBootstrap`, once the engine is built: create the server. A failure
439
+ * fails the boot (ZNIGHT066, the original error as `cause`).
440
+ */
441
+ start(engine: GraphQLEngine, context: GraphQLDriverContext): void | Promise<void>;
442
+ /**
443
+ * Stops the server, after the engine ended its subscriptions: in `GraphQLPlugin.onDispose` for a
444
+ * route-mounted driver (once `HttpPlugin` drained its requests, which the driver keeps serving),
445
+ * in `GraphQLPlugin.onStop` for a server-mounted one (drain and stop the server).
446
+ */
447
+ stop?(): void | Promise<void>;
448
+ }
449
+ export { ScalarDefinition as A, GraphQLResultMapperClass as C, LoaderOptions as D, LoaderKey as E, GraphQLErrorCodeValue as M, GraphQLException as N, LoaderValue as O, GraphQLExceptionOptions as P, GraphQLResultMapper as S, GraphQLTransport as T, GraphQLErrorMapper as _, EngineExecuteOptions as a, GraphQLGuardClass as b, GraphQLRequest as c, orderByKeys as d, BatchLoad as f, GraphQLContext as g, GraphQLArgs as h, GraphQLDriverRoutesContext as i, GraphQLErrorCode as j, ResolverContext as k, GraphQLResultStream as l, BatchLoader as m, GraphQLDriverContext as n, GraphQLEngine as o, BatchLoadClass as p, GraphQLDriverRoute as r, GraphQLEngineSettings as s, GraphQLDriver as t, OrderByKeysOptions as u, GraphQLErrorMapperClass as v, GraphQLResultTypes as w, GraphQLOperationKind as x, GraphQLGuard as y };
450
+ //# sourceMappingURL=driver-DgbpFj4-.d.mts.map