@leaven-graphql/nestjs 0.1.0 → 0.2.1

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,7 +1,7 @@
1
1
  /**
2
2
  * @leaven-graphql/nestjs - Interceptors
3
3
  *
4
- * Copyright 2026 Pegasus Heavy Industries LLC
4
+ * Copyright 2026 Joseph Quinn
5
5
  * Licensed under the Apache License, Version 2.0
6
6
  */
7
7
  import { type NestInterceptor, type ExecutionContext, type CallHandler } from '@nestjs/common';
@@ -10,7 +10,9 @@ import { Reflector } from '@nestjs/core';
10
10
  /**
11
11
  * Logging interceptor for GraphQL operations
12
12
  *
13
- * Logs timing and operation information for debugging.
13
+ * Logs timing and operation information for debugging. Successful
14
+ * resolutions are logged via `console.log`; failures are logged via
15
+ * `console.error` with the error attached, then rethrown unchanged.
14
16
  *
15
17
  * @example
16
18
  * ```typescript
@@ -27,7 +29,12 @@ export declare class LoggingInterceptor implements NestInterceptor {
27
29
  /**
28
30
  * Error formatting interceptor
29
31
  *
30
- * Catches errors and formats them consistently.
32
+ * Catches errors and formats them consistently by delegating to
33
+ * `@leaven-graphql/errors`. Leaven errors keep their message and error
34
+ * code; unexpected errors are masked in production (message replaced,
35
+ * code set to `INTERNAL_ERROR`) and carry their stack trace in
36
+ * `extensions.stackTrace` in development. The caught error is never
37
+ * mutated — a new `GraphQLError` is thrown instead.
31
38
  *
32
39
  * @example
33
40
  * ```typescript
@@ -42,13 +49,35 @@ export declare class ErrorFormattingInterceptor implements NestInterceptor {
42
49
  intercept(_context: ExecutionContext, next: CallHandler): Observable<unknown>;
43
50
  /**
44
51
  * Format an error for GraphQL response
52
+ *
53
+ * Returns a new `GraphQLError` built from the formatted representation
54
+ * rather than mutating the input error.
55
+ *
56
+ * `GraphQLError` derives `locations` from the `nodes`, or the
57
+ * `source`/`positions`, handed to its constructor — never from a `locations`
58
+ * field — so those are carried over from the converted error. Without them a
59
+ * client would lose the source position that the driver's own formatting
60
+ * path (`formatError`) preserves. A masked error reports no locations, and
61
+ * must not leak them here either.
45
62
  */
46
63
  private formatError;
47
64
  }
48
65
  /**
49
66
  * Complexity tracking interceptor
50
67
  *
51
- * Tracks query complexity for monitoring.
68
+ * Tracks query complexity for monitoring. Field complexity declared via
69
+ * `@Complexity()` is accumulated onto the request context's
70
+ * `_resolvedFieldComplexity` key, so multiple fields resolved in one request
71
+ * sum rather than overwrite each other.
72
+ *
73
+ * This measurement is **observational only**. It deliberately does not touch
74
+ * `_queryComplexity`, the key the driver assigns per request and
75
+ * `ComplexityGuard` compares: NestJS runs guards before interceptors, so a
76
+ * value accumulated here can never influence the guard for its own request,
77
+ * and writing to that key would only corrupt the driver's static estimate —
78
+ * making later fields of a query answer for work they did not do. Read
79
+ * `_resolvedFieldComplexity` from logging, metrics, or a plugin after
80
+ * execution.
52
81
  *
53
82
  * @example
54
83
  * ```typescript
@@ -71,7 +100,14 @@ export declare class ComplexityInterceptor implements NestInterceptor {
71
100
  /**
72
101
  * Caching interceptor
73
102
  *
74
- * Handles cache hints for responses.
103
+ * Forwards a `@CacheHint()` to `info.cacheControl.setCacheHint()`.
104
+ *
105
+ * @deprecated Has no effect; removal target 0.3.0. `info.cacheControl` is an
106
+ * Apollo Server construct, and nothing in Leaven's execution path attaches it,
107
+ * so in every supported configuration this interceptor does nothing and
108
+ * `@CacheHint` has no observable effect. Emit `Cache-Control` from the HTTP
109
+ * layer instead. Retained only so existing configurations keep compiling, and
110
+ * so a resolver executed under an Apollo-compatible `info` still gets its hint.
75
111
  *
76
112
  * @example
77
113
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"interceptors.d.ts","sourceRoot":"","sources":["../src/interceptors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAA+B,MAAM,MAAM,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC;;;;;;;;;;;;;GAaG;AACH,qBACa,kBAAmB,YAAW,eAAe;IACjD,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;CAYpF;AAED;;;;;;;;;;;;;GAaG;AACH,qBACa,0BAA2B,YAAW,eAAe;IACzD,SAAS,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;IAUpF;;OAEG;IACH,OAAO,CAAC,WAAW;CAUpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBACa,qBAAsB,YAAW,eAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAE1C,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;CAiBpF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBACa,kBAAmB,YAAW,eAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAE1C,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;CAsBpF;AAED;;;;;;;;;;;;;GAaG;AACH,qBACa,kBAAmB,YAAW,eAAe;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAExD,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;IAmBnF;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;OAEG;IACI,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIlD;;OAEG;IACI,UAAU,IAAI,cAAc,EAAE;IASrC;;OAEG;IACI,KAAK,IAAI,IAAI;CAGrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"interceptors.d.ts","sourceRoot":"","sources":["../src/interceptors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAA+B,MAAM,MAAM,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAMzC;;;;;;;;;;;;;;;GAeG;AACH,qBACa,kBAAmB,YAAW,eAAe;IACjD,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;CAiBpF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,0BAA2B,YAAW,eAAe;IACzD,SAAS,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;IAUpF;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,WAAW;CAiBpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBACa,qBAAsB,YAAW,eAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAE1C,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;CAqBpF;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBACa,kBAAmB,YAAW,eAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAE1C,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;CAuBpF;AAED;;;;;;;;;;;;;GAaG;AACH,qBACa,kBAAmB,YAAW,eAAe;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAExD,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;IAmBnF;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;OAEG;IACI,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIlD;;OAEG;IACI,UAAU,IAAI,cAAc,EAAE;IASrC;;OAEG;IACI,KAAK,IAAI,IAAI;CAGrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * @leaven-graphql/nestjs - Middleware
3
3
  *
4
- * Copyright 2026 Pegasus Heavy Industries LLC
4
+ * Copyright 2026 Joseph Quinn
5
5
  * Licensed under the Apache License, Version 2.0
6
6
  */
7
7
  import { type NestMiddleware, type Type } from '@nestjs/common';
8
8
  interface Request {
9
9
  method: string;
10
10
  path: string;
11
+ /** Full URL including the query string, as populated by Express */
12
+ originalUrl?: string;
13
+ url?: string;
14
+ secure?: boolean;
11
15
  body?: Record<string, unknown>;
12
16
  headers: Record<string, string | string[] | undefined>;
13
17
  }
@@ -26,6 +30,24 @@ import type { LeavenModuleOptions } from './types';
26
30
  *
27
31
  * This middleware intercepts requests to the GraphQL endpoint
28
32
  * and processes them through the Leaven driver.
33
+ *
34
+ * ## Scope of the transport this middleware implements
35
+ *
36
+ * It calls {@link LeavenDriver.execute} directly and deliberately does NOT go
37
+ * through {@link LeavenDriver.handleRequest}, which is an alternative
38
+ * Fetch-native entry point for callers that already hold a `Request`/`Response`
39
+ * pair (a Bun `fetch` handler, for example). Two consequences follow, and both
40
+ * are intentional rather than oversights:
41
+ *
42
+ * - Only `POST` with a JSON body is served. `GET ?query=` is reserved for the
43
+ * playground, and `application/graphql` bodies — which `handleRequest`'s
44
+ * parser does support — are not accepted here.
45
+ * - The context's `req`/`res` are the **Express** request and response, not the
46
+ * Fetch `Request`/`Response` that `handleRequest` stores. This is what makes
47
+ * `ctx.req.user` work under Passport. See {@link GqlContext} for the full
48
+ * contract; a resolver that must work behind either entry point should read
49
+ * values placed on the context by the `context` factory rather than reaching
50
+ * into `ctx.req`.
29
51
  */
30
52
  export declare class GraphQLMiddleware implements NestMiddleware {
31
53
  private readonly driver;
@@ -40,7 +62,10 @@ export declare class GraphQLMiddleware implements NestMiddleware {
40
62
  */
41
63
  private handleGraphQLRequest;
42
64
  /**
43
- * Serve GraphQL Playground HTML
65
+ * Serve GraphiQL HTML
66
+ *
67
+ * Delegates to `@leaven-graphql/playground`, which owns the template and
68
+ * its escaping, rather than maintaining a second copy here.
44
69
  */
45
70
  private servePlayground;
46
71
  /**
@@ -48,9 +73,80 @@ export declare class GraphQLMiddleware implements NestMiddleware {
48
73
  */
49
74
  private handleCorsPreFlight;
50
75
  /**
51
- * Get the playground HTML
76
+ * Resolve the effective CORS configuration, or `null` when this middleware
77
+ * must not emit CORS headers at all.
78
+ *
79
+ * CORS is **opt-in**, matching the `cors` option's documented "Enable CORS"
80
+ * semantics: leaving it unset emits no `Access-Control-*` headers and leaves
81
+ * `OPTIONS` preflights to the application. Only an explicit `true` (or an
82
+ * options object) turns it on — a stock `LeavenModule.forRoot({ schema })`
83
+ * must never answer every GraphQL POST with `Access-Control-Allow-Origin: *`.
84
+ * `false` is equivalent to unset and is accepted for explicitness.
85
+ */
86
+ private resolveCorsOptions;
87
+ /**
88
+ * Apply CORS headers to a response
89
+ *
90
+ * Called for both preflight (OPTIONS) responses and actual GraphQL
91
+ * responses — browsers require `Access-Control-Allow-Origin` on the
92
+ * actual response as well as the preflight. No-op when CORS was not
93
+ * enabled (see {@link resolveCorsOptions}).
94
+ *
95
+ * The allowed origin is *reflected*, never defaulted to `*` when an
96
+ * allowlist is configured: `origin: false` emits no origin header at all,
97
+ * an array emits the request's `Origin` only when the allowlist contains
98
+ * it, and `origin: true` echoes the request's `Origin`. `Vary: Origin` is
99
+ * emitted whenever the value depends on the request so caches cannot serve
100
+ * one origin's response to another, and `*` is never paired with
101
+ * `credentials: true` (browsers reject that combination outright).
102
+ */
103
+ private applyCorsHeaders;
104
+ /**
105
+ * Resolve the `Access-Control-Allow-Origin` value for a request
106
+ *
107
+ * Returns `value: undefined` to mean "emit no origin header", and
108
+ * `varies: true` when the value was derived from the request's `Origin`
109
+ * header and therefore requires `Vary: Origin`.
110
+ */
111
+ private resolveAllowedOrigin;
112
+ /**
113
+ * Adapt an Express-style request into a Fetch API `Request`
114
+ *
115
+ * The `ContextFactory` contract (types.ts) is declared against the global
116
+ * Fetch API types, and `driver.createContext` passes genuine Fetch objects.
117
+ * Adapting here keeps that contract intact **for the factory's first
118
+ * parameter only**, so factories may call `request.headers.get()`,
119
+ * `request.json()`, or read `request.url` regardless of how the request
120
+ * arrived.
121
+ *
122
+ * The factory's second parameter is NOT adapted: it is the transport-native
123
+ * response object, which under this middleware is the Express response.
124
+ * `response.status(...)` is therefore a method rather than a number and
125
+ * `response.headers` does not exist — use `response.setHeader(name, value)`
126
+ * when writing a factory that must work behind `GraphQLMiddleware`.
127
+ *
128
+ * The URL's authority is never taken from an untrusted `Host` header
129
+ * unchecked: `options.publicUrl` wins when configured, otherwise `Host` is
130
+ * accepted only if it looks like a plain authority, and anything else falls
131
+ * back to the canonical default. A factory resolving tenancy from
132
+ * `new URL(request.url).hostname` therefore cannot be steered by a
133
+ * spoofed header, and a hostile value cannot make `new Request()` throw
134
+ * and turn every request into a 500.
135
+ */
136
+ private toFetchRequest;
137
+ /**
138
+ * Resolve the origin (scheme + authority) for the synthesized request URL
139
+ *
140
+ * A configured `publicUrl` always wins; otherwise the `Host` header is used
141
+ * only when it is a plain authority, and anything else falls back to the
142
+ * canonical origin.
143
+ */
144
+ private resolveOrigin;
145
+ /**
146
+ * The trusted origin used when no configured or trustworthy authority is
147
+ * available
52
148
  */
53
- private getPlaygroundHtml;
149
+ private canonicalOrigin;
54
150
  }
55
151
  /**
56
152
  * Create middleware module with proper configuration
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,IAAI,EACV,MAAM,gBAAgB,CAAC;AAGxB,UAAU,OAAO;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;CACxD;AAED,UAAU,QAAQ;IAChB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC9B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC9B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IACjD,GAAG,IAAI,QAAQ,CAAC;CACjB;AAED,KAAK,YAAY,GAAG,MAAM,IAAI,CAAC;AAE/B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;;GAKG;AACH,qBACa,iBAAkB,YAAW,cAAc;IAE7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADf,MAAM,EAAE,YAAY,EACZ,OAAO,EAAE,mBAAmB;IAG9E;;OAEG;IACU,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BhF;;OAEG;YACW,oBAAoB;IAyClC;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwC3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;CA6B1B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAAC,cAAc,CAAC,CAE9D"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,IAAI,EACV,MAAM,gBAAgB,CAAC;AAKxB,UAAU,OAAO;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;CACxD;AAED,UAAU,QAAQ;IAChB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC9B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC9B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IACjD,GAAG,IAAI,QAAQ,CAAC;CACjB;AAED,KAAK,YAAY,GAAG,MAAM,IAAI,CAAC;AAE/B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAUhE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBACa,iBAAkB,YAAW,cAAc;IAE7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADf,MAAM,EAAE,YAAY,EACZ,OAAO,EAAE,mBAAmB;IAG9E;;OAEG;IACU,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAiChF;;OAEG;YACW,oBAAoB;IA4DlC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAK3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,gBAAgB;IAiDxB;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IA2C5B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,OAAO,CAAC,cAAc;IAoCtB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAarB;;;OAGG;IACH,OAAO,CAAC,eAAe;CAIxB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAAC,cAAc,CAAC,CAE9D"}
package/dist/module.d.ts CHANGED
@@ -1,25 +1,51 @@
1
1
  /**
2
2
  * @leaven-graphql/nestjs - NestJS Module
3
3
  *
4
- * Copyright 2026 Pegasus Heavy Industries LLC
4
+ * Copyright 2026 Joseph Quinn
5
5
  * Licensed under the Apache License, Version 2.0
6
6
  */
7
- import { DynamicModule } from '@nestjs/common';
8
- import type { LeavenModuleOptions, LeavenModuleAsyncOptions } from './types';
7
+ import { DynamicModule, type MiddlewareConsumer, type NestModule } from '@nestjs/common';
8
+ import { LEAVEN_MODULE_OPTIONS, LEAVEN_DRIVER, LEAVEN_PUBSUB, type LeavenModuleOptions, type LeavenModuleAsyncOptions } from './types';
9
9
  /**
10
- * Injection token for Leaven module options
11
- */
12
- export declare const LEAVEN_MODULE_OPTIONS = "LEAVEN_MODULE_OPTIONS";
13
- /**
14
- * Injection token for Leaven driver
10
+ * Injection tokens for the Leaven module options and driver.
11
+ *
12
+ * Defined in `./types` (see the note there) and re-exported here so that
13
+ * existing `import { LEAVEN_MODULE_OPTIONS, LEAVEN_DRIVER } from './module'`
14
+ * sites keep working.
15
15
  */
16
- export declare const LEAVEN_DRIVER = "LEAVEN_DRIVER";
16
+ export { LEAVEN_MODULE_OPTIONS, LEAVEN_DRIVER, LEAVEN_PUBSUB };
17
17
  /**
18
18
  * Leaven GraphQL Module for NestJS
19
19
  *
20
20
  * This module provides integration between NestJS and Leaven GraphQL.
21
21
  * It can be used with both schema-first and code-first approaches.
22
22
  *
23
+ * Configuring the module via {@link forRoot} or {@link forRootAsync}
24
+ * registers the {@link LeavenDriver}, the {@link SchemaBuilderService}
25
+ * (which builds the schema from `schema` or `typeDefs`/`resolvers` and
26
+ * hands it to the driver), the {@link SubscriptionManager}, and the
27
+ * {@link GraphQLMiddleware} serving the HTTP endpoint, which is applied
28
+ * automatically at `options.path` (default `/graphql`).
29
+ *
30
+ * ## What this module does NOT wire
31
+ *
32
+ * - **The WebSocket transport.** {@link SubscriptionManager} is registered as a
33
+ * provider, but registering it does not open a socket: opening one requires
34
+ * `server.upgrade()` on the underlying HTTP server, and this module only has
35
+ * a `MiddlewareConsumer`, which cannot express an upgrade route. The manager
36
+ * deliberately owns the graphql-ws protocol and nothing else. Callers
37
+ * register the upgrade route at `subscriptions.path` themselves and hand each
38
+ * socket to the manager — see the package README's "Wiring the WebSocket
39
+ * transport".
40
+ * - **The NestJS execution pipeline around resolvers.** Resolvers supplied via
41
+ * `resolvers` (or baked into a pre-built `schema`) are plain functions
42
+ * invoked by graphql-js, not Nest handlers, so `@UseGuards`,
43
+ * `@UseInterceptors`, and parameter decorators such as `@Args()` never fire
44
+ * for them. That requires a `@nestjs/graphql` `AbstractGraphQLDriver` bridge,
45
+ * which this package does not ship. `maxComplexity` is unaffected — the
46
+ * executor enforces it directly — but `maxDepth`, which is only surfaced for
47
+ * {@link DepthGuard}, is not enforced on this path.
48
+ *
23
49
  * @example
24
50
  * ```typescript
25
51
  * // Schema-first approach
@@ -59,17 +85,56 @@ export declare const LEAVEN_DRIVER = "LEAVEN_DRIVER";
59
85
  * export class AppModule {}
60
86
  * ```
61
87
  */
62
- export declare class LeavenModule {
88
+ export declare class LeavenModule implements NestModule {
89
+ private readonly options?;
90
+ constructor(options?: LeavenModuleOptions | undefined);
63
91
  /**
64
- * Configure the module synchronously
92
+ * Register the {@link GraphQLMiddleware} at the configured GraphQL
93
+ * endpoint (`options.path`, default `/graphql`).
94
+ *
95
+ * Called automatically by NestJS during application initialization. When
96
+ * the module is imported without `forRoot()`/`forRootAsync()` no options
97
+ * provider exists (and no GraphQL providers are registered), so nothing
98
+ * is wired.
99
+ */
100
+ configure(consumer: MiddlewareConsumer): void;
101
+ /**
102
+ * Configure the module synchronously.
103
+ *
104
+ * Registers the options and driver providers along with the
105
+ * {@link SchemaBuilderService}, {@link SubscriptionManager}, and
106
+ * {@link GraphQLMiddleware} so that importing this module yields a
107
+ * working GraphQL endpoint.
65
108
  */
66
109
  static forRoot(options?: LeavenModuleOptions): DynamicModule;
67
110
  /**
68
- * Configure the module asynchronously
111
+ * Build the providers backing {@link ComplexityGuard} and
112
+ * {@link DepthGuard}.
113
+ *
114
+ * The guards inject their limits by token, so the tokens must resolve
115
+ * even when the corresponding option is absent; `Infinity` expresses
116
+ * "no limit" without the guards having to special-case `undefined`.
117
+ */
118
+ private static createLimitProviders;
119
+ /**
120
+ * Build the shared {@link PubSub} provider resolved by `@InjectPubSub()`.
121
+ *
122
+ * One instance per module so that a publisher and a subscriber in
123
+ * different providers reach the same topics.
124
+ */
125
+ private static createPubSubProvider;
126
+ /**
127
+ * Configure the module asynchronously.
128
+ *
129
+ * Registers the same providers as {@link forRoot}, with the options
130
+ * resolved via `useFactory`, `useClass`, or `useExisting`.
69
131
  */
70
132
  static forRootAsync(options: LeavenModuleAsyncOptions): DynamicModule;
71
133
  /**
72
134
  * Create async providers based on configuration
135
+ *
136
+ * @throws Error when none of `useFactory`, `useClass`, or `useExisting`
137
+ * is supplied.
73
138
  */
74
139
  private static createAsyncProviders;
75
140
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,aAAa,EAKd,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,mBAAmB,EACnB,wBAAwB,EAEzB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBAEa,YAAY;IACvB;;OAEG;WACW,OAAO,CAAC,OAAO,GAAE,mBAAwB,GAAG,aAAa;IAmBvE;;OAEG;WACW,YAAY,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa;IAiB5E;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAiBnC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,0BAA0B;CAsB1C"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,aAAa,EAKb,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAGhB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAE9B,MAAM,SAAS,CAAC;AAQjB;;;;;;GAMG;AACH,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AACH,qBAEa,YAAa,YAAW,UAAU;IAI3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAR,OAAO,CAAC,EAAE,mBAAmB,YAAA;IAGhD;;;;;;;;OAQG;IACI,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAUpD;;;;;;;OAOG;WACW,OAAO,CAAC,OAAO,GAAE,mBAAwB,GAAG,aAAa;IAmCvE;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAiBnC;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IASnC;;;;;OAKG;WACW,YAAY,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa;IAiC5E;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAuBnC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,0BAA0B;CAsB1C"}
@@ -1,25 +1,35 @@
1
1
  /**
2
2
  * @leaven-graphql/nestjs - Schema Builder Integration
3
3
  *
4
- * Provides integration with NestJS's code-first schema building approach.
4
+ * Builds the module's `GraphQLSchema` from a pre-built `schema` or from SDL
5
+ * `typeDefs` (optionally merged with a `resolvers` map) and hands it to the
6
+ * driver. Code-first generation (`autoSchemaFile`) is not implemented and
7
+ * fails at bootstrap rather than silently.
5
8
  *
6
- * Copyright 2026 Pegasus Heavy Industries LLC
9
+ * Copyright 2026 Joseph Quinn
7
10
  * Licensed under the Apache License, Version 2.0
8
11
  */
9
12
  import { type OnModuleInit, type Type } from '@nestjs/common';
10
- import type { GraphQLSchema } from 'graphql';
13
+ import { type DocumentNode, type GraphQLSchema } from 'graphql';
11
14
  import { LeavenDriver } from './driver';
12
15
  import type { LeavenModuleOptions, BuildSchemaOptions } from './types';
16
+ /**
17
+ * Resolver map as accepted by {@link LeavenModuleOptions.resolvers}.
18
+ */
19
+ type ResolverMap = Record<string, unknown> | Array<Record<string, unknown>>;
13
20
  /**
14
21
  * Schema source type - determines how to build the schema
22
+ *
23
+ * {@link SchemaBuilderService.buildSchema} narrows on a value of this type,
24
+ * so every supported configuration is enumerated here exactly once.
15
25
  */
16
26
  export type SchemaSource = {
17
27
  type: 'provided';
18
28
  schema: GraphQLSchema;
19
29
  } | {
20
30
  type: 'typeDefs';
21
- typeDefs: string;
22
- resolvers: Record<string, unknown>;
31
+ typeDefs: string | DocumentNode | Array<string | DocumentNode>;
32
+ resolvers?: ResolverMap;
23
33
  } | {
24
34
  type: 'autoSchema';
25
35
  options: BuildSchemaOptions;
@@ -39,22 +49,36 @@ export declare class SchemaBuilderService implements OnModuleInit {
39
49
  * Initialize the schema on module init
40
50
  */
41
51
  onModuleInit(): Promise<void>;
52
+ /**
53
+ * Classify the configured options into a {@link SchemaSource}.
54
+ *
55
+ * `schema` wins over `typeDefs`, which wins over `autoSchemaFile`.
56
+ * Returns `null` when no schema source is configured at all.
57
+ */
58
+ private resolveSchemaSource;
42
59
  /**
43
60
  * Build the schema from configured sources
61
+ *
62
+ * @throws {Error} When only `autoSchemaFile` is configured. Leaven has no
63
+ * code-first pipeline, so returning `null` here would leave the driver
64
+ * without a schema and fail every request at runtime; failing at bootstrap
65
+ * points at the actual misconfiguration instead.
44
66
  */
45
67
  buildSchema(): Promise<GraphQLSchema | null>;
46
68
  /**
47
69
  * Build schema from type definitions (SDL)
70
+ *
71
+ * SDL alone is a complete schema and is built with graphql-js directly.
72
+ * When a resolver map is supplied it is merged in with
73
+ * `makeExecutableSchema`, after which any `@Description`/`@Deprecated`
74
+ * metadata recorded on the resolver functions is applied to the matching
75
+ * schema fields.
48
76
  */
49
77
  private buildFromTypeDefs;
50
78
  /**
51
- * Merge multiple type definition strings
79
+ * Normalize type definitions to a single SDL string
52
80
  */
53
81
  private mergeTypeDefs;
54
- /**
55
- * Merge multiple resolver objects
56
- */
57
- private mergeResolvers;
58
82
  /**
59
83
  * Get the built schema
60
84
  */
@@ -87,6 +111,10 @@ export interface SchemaFileOptions {
87
111
  }
88
112
  /**
89
113
  * Generate a schema file from the current schema
114
+ *
115
+ * Creates the parent directory if it does not exist. When
116
+ * `includeDescriptions` is `false`, all type, field, and argument
117
+ * descriptions are stripped from the emitted SDL.
90
118
  */
91
119
  export declare function generateSchemaFile(schema: GraphQLSchema, options: SchemaFileOptions): Promise<void>;
92
120
  /**
@@ -116,4 +144,5 @@ export declare function registerResolverMetadata(metadata: ResolverMetadata): vo
116
144
  * Clear resolver metadata (for testing)
117
145
  */
118
146
  export declare function clearResolverMetadata(): void;
147
+ export {};
119
148
  //# sourceMappingURL=schema-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../src/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAc,KAAK,YAAY,EAAU,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEvE;;GAEG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC1E;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAExD;;;;;GAKG;AACH,qBACa,oBAAqB,YAAW,YAAY;IAItB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJhD,OAAO,CAAC,WAAW,CAA8B;gBAGC,OAAO,EAAE,mBAAmB,EACpC,MAAM,EAAE,YAAY;IAG9D;;OAEG;IACU,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C;;OAEG;IACU,WAAW,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAqBzD;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA4CzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAuBrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACI,SAAS,IAAI,aAAa,GAAG,IAAI;IAIxC;;OAEG;IACI,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAK7C;;OAEG;IACI,aAAa,IAAI,OAAO;CAGhC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,cAAc,GAAG,OAAO,CAAC;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;IACnC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;QAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AAOD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,EAAE,CAExD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAEzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
1
+ {"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../src/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAc,KAAK,YAAY,EAAU,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClF,OAAO,EAOL,KAAK,YAAY,EAGjB,KAAK,aAAa,EACnB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEvE;;GAEG;AACH,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAC3C;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC;IAC/D,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB,GACD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAExD;;;;;GAKG;AACH,qBACa,oBAAqB,YAAW,YAAY;IAItB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJhD,OAAO,CAAC,WAAW,CAA8B;gBAGC,OAAO,EAAE,mBAAmB,EACpC,MAAM,EAAE,YAAY;IAG9D;;OAEG;IACU,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;;;;;;OAOG;IACU,WAAW,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAqBzD;;;;;;;;OAQG;YACW,iBAAiB;IAqB/B;;OAEG;IACH,OAAO,CAAC,aAAa;IAiBrB;;OAEG;IACI,SAAS,IAAI,aAAa,GAAG,IAAI;IAIxC;;OAEG;IACI,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAK7C;;OAEG;IACI,aAAa,IAAI,OAAO;CAGhC;AA2ED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAmBD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,cAAc,GAAG,OAAO,CAAC;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;IACnC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;QAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AAOD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,EAAE,CAExD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAEzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}