@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.
- package/LICENSE +1 -1
- package/README.md +408 -23
- package/dist/decorators.d.ts +92 -3
- package/dist/decorators.d.ts.map +1 -1
- package/dist/driver.d.ts +107 -8
- package/dist/driver.d.ts.map +1 -1
- package/dist/execution-context.d.ts +46 -2
- package/dist/execution-context.d.ts.map +1 -1
- package/dist/graphql-driver.d.ts +180 -0
- package/dist/graphql-driver.d.ts.map +1 -0
- package/dist/guards.d.ts +57 -11
- package/dist/guards.d.ts.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1577 -792
- package/dist/interceptors.d.ts +41 -5
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/middleware.d.ts +100 -4
- package/dist/middleware.d.ts.map +1 -1
- package/dist/module.d.ts +77 -12
- package/dist/module.d.ts.map +1 -1
- package/dist/schema-builder.d.ts +39 -10
- package/dist/schema-builder.d.ts.map +1 -1
- package/dist/subscriptions.d.ts +346 -22
- package/dist/subscriptions.d.ts.map +1 -1
- package/dist/types.d.ts +188 -18
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -12
package/dist/decorators.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @leaven-graphql/nestjs - Decorators
|
|
3
3
|
*
|
|
4
|
-
* Copyright 2026
|
|
4
|
+
* Copyright 2026 Joseph Quinn
|
|
5
5
|
* Licensed under the Apache License, Version 2.0
|
|
6
6
|
*/
|
|
7
|
+
import { type ExecutionContext } from '@nestjs/common';
|
|
7
8
|
/**
|
|
8
9
|
* Metadata key for resolver complexity
|
|
9
10
|
*/
|
|
@@ -24,6 +25,12 @@ export declare const CACHE_KEY = "leaven:cache";
|
|
|
24
25
|
* Metadata key for subscription filter
|
|
25
26
|
*/
|
|
26
27
|
export declare const SUBSCRIPTION_FILTER_KEY = "leaven:subscription:filter";
|
|
28
|
+
/**
|
|
29
|
+
* Extractor backing the {@link Context} decorator.
|
|
30
|
+
*
|
|
31
|
+
* @internal Exported for testing only.
|
|
32
|
+
*/
|
|
33
|
+
export declare function contextExtractor(data: string | undefined, ctx: ExecutionContext): unknown;
|
|
27
34
|
/**
|
|
28
35
|
* Inject the GraphQL context
|
|
29
36
|
*
|
|
@@ -35,7 +42,13 @@ export declare const SUBSCRIPTION_FILTER_KEY = "leaven:subscription:filter";
|
|
|
35
42
|
* }
|
|
36
43
|
* ```
|
|
37
44
|
*/
|
|
38
|
-
export declare const Context: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>>)[]) => ParameterDecorator;
|
|
45
|
+
export declare const Context: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>> | undefined)[]) => ParameterDecorator;
|
|
46
|
+
/**
|
|
47
|
+
* Extractor backing the {@link Info} decorator.
|
|
48
|
+
*
|
|
49
|
+
* @internal Exported for testing only.
|
|
50
|
+
*/
|
|
51
|
+
export declare function infoExtractor(_data: unknown, ctx: ExecutionContext): unknown;
|
|
39
52
|
/**
|
|
40
53
|
* Inject the GraphQL info object
|
|
41
54
|
*
|
|
@@ -48,6 +61,12 @@ export declare const Context: (...dataOrPipes: (string | import("@nestjs/common"
|
|
|
48
61
|
* ```
|
|
49
62
|
*/
|
|
50
63
|
export declare const Info: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
64
|
+
/**
|
|
65
|
+
* Extractor backing the {@link Root} decorator.
|
|
66
|
+
*
|
|
67
|
+
* @internal Exported for testing only.
|
|
68
|
+
*/
|
|
69
|
+
export declare function rootExtractor(_data: unknown, ctx: ExecutionContext): unknown;
|
|
51
70
|
/**
|
|
52
71
|
* Inject the root/parent value
|
|
53
72
|
*
|
|
@@ -64,6 +83,12 @@ export declare const Root: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
|
64
83
|
* Alias for Root decorator
|
|
65
84
|
*/
|
|
66
85
|
export declare const Parent: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
86
|
+
/**
|
|
87
|
+
* Extractor backing the {@link Args} decorator.
|
|
88
|
+
*
|
|
89
|
+
* @internal Exported for testing only.
|
|
90
|
+
*/
|
|
91
|
+
export declare function argsExtractor(data: string | undefined, ctx: ExecutionContext): unknown;
|
|
67
92
|
/**
|
|
68
93
|
* Inject resolver arguments
|
|
69
94
|
*
|
|
@@ -75,7 +100,7 @@ export declare const Parent: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
|
75
100
|
* }
|
|
76
101
|
* ```
|
|
77
102
|
*/
|
|
78
|
-
export declare const Args: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>>)[]) => ParameterDecorator;
|
|
103
|
+
export declare const Args: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>> | undefined)[]) => ParameterDecorator;
|
|
79
104
|
/**
|
|
80
105
|
* Set complexity for a resolver
|
|
81
106
|
*
|
|
@@ -104,6 +129,13 @@ export interface ComplexityEstimatorArgs {
|
|
|
104
129
|
/**
|
|
105
130
|
* Mark a field as deprecated
|
|
106
131
|
*
|
|
132
|
+
* @remarks
|
|
133
|
+
* Records the reason under {@link DEPRECATED_KEY}. `SchemaBuilderService`
|
|
134
|
+
* reads it off the resolver functions passed via the `resolvers` option and
|
|
135
|
+
* sets the matching field's `deprecationReason`, so the field is marked
|
|
136
|
+
* `@deprecated` in the emitted schema. A pre-built `schema` is used as
|
|
137
|
+
* given, so annotate its fields there instead.
|
|
138
|
+
*
|
|
107
139
|
* @example
|
|
108
140
|
* ```typescript
|
|
109
141
|
* @Query(() => String)
|
|
@@ -117,6 +149,13 @@ export declare function Deprecated(reason: string): MethodDecorator;
|
|
|
117
149
|
/**
|
|
118
150
|
* Add description to a field
|
|
119
151
|
*
|
|
152
|
+
* @remarks
|
|
153
|
+
* Records the text under {@link DESCRIPTION_KEY}. `SchemaBuilderService`
|
|
154
|
+
* reads it off the resolver functions passed via the `resolvers` option and
|
|
155
|
+
* sets the matching field's description, so the text appears in the emitted
|
|
156
|
+
* schema and in introspection. A pre-built `schema` is used as given, so
|
|
157
|
+
* describe its fields there instead.
|
|
158
|
+
*
|
|
120
159
|
* @example
|
|
121
160
|
* ```typescript
|
|
122
161
|
* @Query(() => User)
|
|
@@ -130,6 +169,11 @@ export declare function Description(text: string): MethodDecorator;
|
|
|
130
169
|
/**
|
|
131
170
|
* Enable caching for a resolver
|
|
132
171
|
*
|
|
172
|
+
* @deprecated Has no effect; removal target 0.3.0. The hint is only applied by
|
|
173
|
+
* `CachingInterceptor`, which requires `info.cacheControl` — an Apollo Server
|
|
174
|
+
* construct that Leaven never attaches to `GraphQLResolveInfo`. The metadata is
|
|
175
|
+
* recorded under {@link CACHE_KEY} but nothing in the library reads it.
|
|
176
|
+
*
|
|
133
177
|
* @example
|
|
134
178
|
* ```typescript
|
|
135
179
|
* @Query(() => Settings)
|
|
@@ -153,9 +197,50 @@ export interface CacheHintOptions {
|
|
|
153
197
|
*/
|
|
154
198
|
scope?: 'PUBLIC' | 'PRIVATE';
|
|
155
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Yield only the source events a predicate accepts.
|
|
202
|
+
*
|
|
203
|
+
* Cancelling the returned iterator cancels the source, because `for await`
|
|
204
|
+
* calls `return()` on the source when the loop completes abruptly — including
|
|
205
|
+
* when the consumer cancels this generator. No extra teardown is performed:
|
|
206
|
+
* a second `return()` would both replace any in-flight error with a cleanup
|
|
207
|
+
* error and, for shared engines, release a topic subscription twice.
|
|
208
|
+
*
|
|
209
|
+
* A predicate that throws drops the offending event and is reported to
|
|
210
|
+
* `console.error` rather than tearing down the whole subscription.
|
|
211
|
+
*
|
|
212
|
+
* @internal Shared by {@link SubscriptionFilter} and the `filter` option of
|
|
213
|
+
* the `Subscription` decorator.
|
|
214
|
+
*/
|
|
215
|
+
export declare function filterAsyncIterator<T>(source: AsyncIterable<T>, predicate: (payload: T) => boolean | Promise<boolean>): AsyncGenerator<T>;
|
|
216
|
+
/**
|
|
217
|
+
* Replace a method descriptor with one that filters the async iterable the
|
|
218
|
+
* original method produces.
|
|
219
|
+
*
|
|
220
|
+
* The wrapper is deliberately synchronous: when the original method returns
|
|
221
|
+
* an async iterable directly, so does the wrapper, so a direct caller can
|
|
222
|
+
* still write `for await (const x of resolver.messageAdded())`. Only when the
|
|
223
|
+
* original returns a promise does the wrapper return one.
|
|
224
|
+
*
|
|
225
|
+
* @internal Shared by {@link SubscriptionFilter} and the `filter` option of
|
|
226
|
+
* the `Subscription` decorator, so a correctness fix lands in one place.
|
|
227
|
+
*/
|
|
228
|
+
export declare function wrapWithFilter(descriptor: PropertyDescriptor, filter: (payload: unknown, variables: unknown, context: unknown) => boolean | Promise<boolean>): void;
|
|
156
229
|
/**
|
|
157
230
|
* Add filter to subscription
|
|
158
231
|
*
|
|
232
|
+
* Wraps the decorated method so the async iterator it returns yields only
|
|
233
|
+
* the events the predicate accepts. The predicate receives the published
|
|
234
|
+
* payload along with the resolver's arguments and context, and may be
|
|
235
|
+
* asynchronous. Rejected events are dropped without reaching the client.
|
|
236
|
+
*
|
|
237
|
+
* The wrapper preserves the original method's return kind: an async iterable
|
|
238
|
+
* stays an async iterable (so direct callers can `for await` it), and a
|
|
239
|
+
* promise stays a promise.
|
|
240
|
+
*
|
|
241
|
+
* The predicate is also recorded under {@link SUBSCRIPTION_FILTER_KEY} so a
|
|
242
|
+
* schema builder can inspect it.
|
|
243
|
+
*
|
|
159
244
|
* @example
|
|
160
245
|
* ```typescript
|
|
161
246
|
* @Subscription(() => Comment)
|
|
@@ -190,6 +275,10 @@ export declare function Decorators(...decorators: MethodDecorator[]): MethodDeco
|
|
|
190
275
|
/**
|
|
191
276
|
* Create a custom context decorator
|
|
192
277
|
*
|
|
278
|
+
* The underlying param decorator is built once per created decorator, not on
|
|
279
|
+
* every application, so applying the returned decorator at many call sites
|
|
280
|
+
* reuses a single factory.
|
|
281
|
+
*
|
|
193
282
|
* @example
|
|
194
283
|
* ```typescript
|
|
195
284
|
* export const CurrentUser = createContextDecorator<User>('user');
|
package/dist/decorators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
|
package/dist/driver.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @leaven-graphql/nestjs - Leaven Driver for NestJS GraphQL
|
|
3
3
|
*
|
|
4
|
-
* Copyright 2026
|
|
4
|
+
* Copyright 2026 Joseph Quinn
|
|
5
5
|
* Licensed under the Apache License, Version 2.0
|
|
6
6
|
*/
|
|
7
7
|
import { type OnModuleInit, type OnModuleDestroy } from '@nestjs/common';
|
|
8
|
-
import type
|
|
8
|
+
import { type GraphQLSchema, type DocumentNode } from 'graphql';
|
|
9
9
|
import { LeavenExecutor } from '@leaven-graphql/core';
|
|
10
10
|
import type { LeavenModuleOptions, GqlContext, GraphQLFormattedError } from './types';
|
|
11
11
|
/**
|
|
12
12
|
* Request handler result
|
|
13
13
|
*/
|
|
14
|
-
export interface HandlerResult {
|
|
15
|
-
data?:
|
|
14
|
+
export interface HandlerResult<TData = Record<string, unknown>> {
|
|
15
|
+
data?: TData | null;
|
|
16
16
|
errors?: readonly GraphQLFormattedError[];
|
|
17
17
|
extensions?: Record<string, unknown>;
|
|
18
18
|
}
|
|
@@ -26,6 +26,13 @@ export declare class LeavenDriver implements OnModuleInit, OnModuleDestroy {
|
|
|
26
26
|
private executor;
|
|
27
27
|
private schema;
|
|
28
28
|
private readonly options;
|
|
29
|
+
/** Maximum number of memoized query analyses retained */
|
|
30
|
+
private static readonly MAX_ANALYSIS_ENTRIES;
|
|
31
|
+
/**
|
|
32
|
+
* LRU of {@link QueryAnalysis} keyed by query string and operation name.
|
|
33
|
+
* Cleared whenever the schema changes, since complexity is schema-dependent.
|
|
34
|
+
*/
|
|
35
|
+
private readonly analysisCache;
|
|
29
36
|
constructor(options: LeavenModuleOptions);
|
|
30
37
|
/**
|
|
31
38
|
* Initialize the driver with a schema
|
|
@@ -33,6 +40,11 @@ export declare class LeavenDriver implements OnModuleInit, OnModuleDestroy {
|
|
|
33
40
|
onModuleInit(): Promise<void>;
|
|
34
41
|
/**
|
|
35
42
|
* Cleanup resources on module destroy
|
|
43
|
+
*
|
|
44
|
+
* `LeavenExecutor.clearCaches()` is genuinely asynchronous for a remote
|
|
45
|
+
* cache (Redis `FLUSH`), so it is awaited: dropping the promise would both
|
|
46
|
+
* leave the clear unfinished and turn a rejecting cache into an unhandled
|
|
47
|
+
* rejection during shutdown.
|
|
36
48
|
*/
|
|
37
49
|
onModuleDestroy(): Promise<void>;
|
|
38
50
|
/**
|
|
@@ -49,18 +61,61 @@ export declare class LeavenDriver implements OnModuleInit, OnModuleDestroy {
|
|
|
49
61
|
getExecutor(): LeavenExecutor | null;
|
|
50
62
|
/**
|
|
51
63
|
* Initialize the Leaven executor
|
|
64
|
+
*
|
|
65
|
+
* Every option the executor understands is forwarded here. In particular
|
|
66
|
+
* `introspection` and `maxDepth` are *enforced by the executor* — the former
|
|
67
|
+
* by validating with `NoSchemaIntrospectionCustomRule`, the latter by
|
|
68
|
+
* rejecting over-deep documents at parse time — so omitting either would
|
|
69
|
+
* leave a documented option silently inert.
|
|
52
70
|
*/
|
|
53
71
|
private initializeExecutor;
|
|
54
72
|
/**
|
|
55
73
|
* Execute a GraphQL request
|
|
74
|
+
*
|
|
75
|
+
* When `maxComplexity` or `maxDepth` is configured, the query is statically
|
|
76
|
+
* analyzed before execution and its complexity and depth are recorded on the
|
|
77
|
+
* provided context as `_queryComplexity` and `_queryDepth`, the keys read by
|
|
78
|
+
* `ComplexityGuard` and `DepthGuard`. With neither limit configured the
|
|
79
|
+
* analysis is skipped — see {@link attachQueryAnalysis}.
|
|
56
80
|
*/
|
|
57
81
|
execute<TData = Record<string, unknown>>(query: string | DocumentNode, variables?: Record<string, unknown>, context?: GqlContext, operationName?: string): Promise<HandlerResult>;
|
|
82
|
+
/**
|
|
83
|
+
* Turn an error thrown out of the executor into a response error.
|
|
84
|
+
*
|
|
85
|
+
* Leaven errors keep their code, extensions, and message as-is. Unexpected
|
|
86
|
+
* errors are masked only in production — masking them in development would
|
|
87
|
+
* hide the real message from the developer — and
|
|
88
|
+
* `includeStacktraceInErrorResponses` suppresses masking outright, since a
|
|
89
|
+
* masked error carries no stack to include. A configured `formatError` gets
|
|
90
|
+
* the last word, exactly as it does for errors the executor reports itself.
|
|
91
|
+
*/
|
|
92
|
+
private formatThrownError;
|
|
93
|
+
/**
|
|
94
|
+
* Start a GraphQL subscription.
|
|
95
|
+
*
|
|
96
|
+
* Resolves to an async iterator of results when the operation subscribes
|
|
97
|
+
* successfully, or to a {@link HandlerResult} carrying `errors` when the
|
|
98
|
+
* document fails to parse or validate. Callers must distinguish the two by
|
|
99
|
+
* testing for `Symbol.asyncIterator`. This method rejects only when the
|
|
100
|
+
* driver has no executor — every error raised while subscribing is reported
|
|
101
|
+
* through `errors`, and masked in production, exactly as in {@link execute}.
|
|
102
|
+
*
|
|
103
|
+
* As with {@link execute}, complexity and depth are recorded on the context
|
|
104
|
+
* — when a limit is configured — so the guards apply to subscriptions as
|
|
105
|
+
* well as queries.
|
|
106
|
+
*/
|
|
107
|
+
subscribe<TData = Record<string, unknown>>(query: string | DocumentNode, variables?: Record<string, unknown>, context?: GqlContext, operationName?: string): Promise<AsyncIterableIterator<HandlerResult<TData>> | HandlerResult<TData>>;
|
|
58
108
|
/**
|
|
59
109
|
* Handle an HTTP request
|
|
60
110
|
*/
|
|
61
111
|
handleRequest(request: Request, response: Response): Promise<HandlerResult>;
|
|
62
112
|
/**
|
|
63
113
|
* Parse the request body
|
|
114
|
+
*
|
|
115
|
+
* POST requests are dispatched on their content type: `application/json`
|
|
116
|
+
* bodies carry `{ query, variables, operationName }`, and
|
|
117
|
+
* `application/graphql` bodies (per the GraphQL-over-HTTP spec) carry the
|
|
118
|
+
* raw query string. Other methods read the operation from URL parameters.
|
|
64
119
|
*/
|
|
65
120
|
private parseRequestBody;
|
|
66
121
|
/**
|
|
@@ -68,15 +123,55 @@ export declare class LeavenDriver implements OnModuleInit, OnModuleDestroy {
|
|
|
68
123
|
*/
|
|
69
124
|
private createContext;
|
|
70
125
|
/**
|
|
71
|
-
*
|
|
72
|
-
|
|
73
|
-
|
|
126
|
+
* Analyze the query and record its complexity and depth on the request
|
|
127
|
+
* context under the `_queryComplexity` / `_queryDepth` keys that
|
|
128
|
+
* `ComplexityGuard` and `DepthGuard` enforce.
|
|
129
|
+
*
|
|
130
|
+
* Analysis is **skipped entirely** unless `maxComplexity` or `maxDepth` is
|
|
131
|
+
* configured. It costs a full uncached `parse()` plus a full uncached
|
|
132
|
+
* complexity compilation on top of the executor's own (LRU-cached) parse and
|
|
133
|
+
* compile — roughly 2.6x the work of the request itself for a small query —
|
|
134
|
+
* and with no limit configured both guards receive the module's `Infinity`
|
|
135
|
+
* sentinel and have nothing to enforce.
|
|
136
|
+
*
|
|
137
|
+
* When a limit *is* configured, the result is memoized in a small LRU keyed
|
|
138
|
+
* by query text and operation name, so the surcharge is paid once per
|
|
139
|
+
* distinct document rather than once per request.
|
|
140
|
+
*
|
|
141
|
+
* A document that fails to parse is left to the executor, which reports the
|
|
142
|
+
* syntax error as a proper GraphQL error response — no limit can be
|
|
143
|
+
* meaningfully enforced on a document that never executes.
|
|
144
|
+
*
|
|
145
|
+
* Every other failure (a broken compiler, a missing schema, a depth
|
|
146
|
+
* traversal that throws) is an *analysis* failure: it is logged and both
|
|
147
|
+
* fields are set to `Infinity` so the guards fail closed. Leaving them unset
|
|
148
|
+
* would hand an attacker who finds one query shape that trips the analyzer
|
|
149
|
+
* an unlimited-complexity channel. Failures are never memoized.
|
|
150
|
+
*/
|
|
151
|
+
private attachQueryAnalysis;
|
|
152
|
+
/**
|
|
153
|
+
* Store an analysis result, evicting the least recently used entry when the
|
|
154
|
+
* cache is full.
|
|
155
|
+
*/
|
|
156
|
+
private memoizeAnalysis;
|
|
157
|
+
/**
|
|
158
|
+
* Record a fail-closed sentinel for both analysis fields and log the cause.
|
|
159
|
+
*
|
|
160
|
+
* `Infinity` exceeds every finite limit, so a configured `ComplexityGuard`
|
|
161
|
+
* or `DepthGuard` rejects the request rather than letting it through
|
|
162
|
+
* unmeasured.
|
|
163
|
+
*/
|
|
164
|
+
private recordAnalysisFailure;
|
|
74
165
|
/**
|
|
75
166
|
* Check if playground should be enabled
|
|
76
167
|
*/
|
|
77
168
|
isPlaygroundEnabled(): boolean;
|
|
78
169
|
/**
|
|
79
170
|
* Check if introspection should be enabled
|
|
171
|
+
*
|
|
172
|
+
* This is the value handed to the executor as `ExecutorConfig.introspection`,
|
|
173
|
+
* which installs `NoSchemaIntrospectionCustomRule` when it is `false`, so a
|
|
174
|
+
* caller may also use it to decide whether to serve a schema-browsing UI.
|
|
80
175
|
*/
|
|
81
176
|
isIntrospectionEnabled(): boolean;
|
|
82
177
|
/**
|
|
@@ -89,7 +184,11 @@ export declare class LeavenDriver implements OnModuleInit, OnModuleDestroy {
|
|
|
89
184
|
getCacheStats(): ReturnType<LeavenExecutor['getCacheStats']> | null;
|
|
90
185
|
/**
|
|
91
186
|
* Clear all caches
|
|
187
|
+
*
|
|
188
|
+
* Awaited rather than fired and forgotten: `clearCaches` is asynchronous for
|
|
189
|
+
* a remote cache, so a floated promise would leave the clear unfinished and
|
|
190
|
+
* a rejecting cache unhandled.
|
|
92
191
|
*/
|
|
93
|
-
clearCaches(): void
|
|
192
|
+
clearCaches(): Promise<void>;
|
|
94
193
|
}
|
|
95
194
|
//# sourceMappingURL=driver.d.ts.map
|
package/dist/driver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,12 +4,34 @@
|
|
|
4
4
|
* Provides a helper class for accessing GraphQL-specific context in guards,
|
|
5
5
|
* interceptors, and other NestJS constructs.
|
|
6
6
|
*
|
|
7
|
-
* Copyright 2026
|
|
7
|
+
* Copyright 2026 Joseph Quinn
|
|
8
8
|
* Licensed under the Apache License, Version 2.0
|
|
9
9
|
*/
|
|
10
10
|
import type { ExecutionContext, ArgumentsHost } from '@nestjs/common';
|
|
11
11
|
import type { GraphQLResolveInfo } from 'graphql';
|
|
12
12
|
import type { GqlContext } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* Positional indices of the GraphQL resolver arguments as exposed by
|
|
15
|
+
* NestJS's `ExecutionContext.getArgs()` for GraphQL invocations:
|
|
16
|
+
* `[root, args, context, info]`.
|
|
17
|
+
*
|
|
18
|
+
* This is the single source of truth for the positional assumption shared by
|
|
19
|
+
* the execution-context helpers and the param decorators. Use these named
|
|
20
|
+
* indices instead of hard-coded numbers so a change in argument ordering only
|
|
21
|
+
* needs to be reflected in one place.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export declare const GQL_RESOLVER_ARGS: {
|
|
26
|
+
/** The root/parent value — 1st resolver argument */
|
|
27
|
+
readonly ROOT: 0;
|
|
28
|
+
/** The resolver arguments object — 2nd resolver argument */
|
|
29
|
+
readonly ARGS: 1;
|
|
30
|
+
/** The GraphQL context object — 3rd resolver argument */
|
|
31
|
+
readonly CONTEXT: 2;
|
|
32
|
+
/** The `GraphQLResolveInfo` object — 4th resolver argument */
|
|
33
|
+
readonly INFO: 3;
|
|
34
|
+
};
|
|
13
35
|
/**
|
|
14
36
|
* GraphQL execution context helper
|
|
15
37
|
*
|
|
@@ -120,11 +142,33 @@ export declare class GqlExecutionContext {
|
|
|
120
142
|
*/
|
|
121
143
|
getVariables<T = Record<string, unknown>>(): T;
|
|
122
144
|
/**
|
|
123
|
-
* Get the path to the current field
|
|
145
|
+
* Get the field-name path to the current field
|
|
146
|
+
*
|
|
147
|
+
* List indices are omitted, so `users[3].email` yields
|
|
148
|
+
* `['users', 'email']` — the same value this method has always returned,
|
|
149
|
+
* which keeps `getPath().join('.')` stable as a metric label, log key, or
|
|
150
|
+
* cache key.
|
|
151
|
+
*
|
|
152
|
+
* @deprecated Use {@link getFullPath}, which preserves list indices and so
|
|
153
|
+
* identifies an individual element rather than a field position.
|
|
124
154
|
*/
|
|
125
155
|
getPath(): string[];
|
|
156
|
+
/**
|
|
157
|
+
* Get the full path to the current field
|
|
158
|
+
*
|
|
159
|
+
* Every path segment is included: field names as strings and list indices
|
|
160
|
+
* as numbers, so `users[3].email` yields `['users', 3, 'email']` and list
|
|
161
|
+
* positions remain distinguishable from field names.
|
|
162
|
+
*/
|
|
163
|
+
getFullPath(): Array<string | number>;
|
|
126
164
|
/**
|
|
127
165
|
* Get the requested fields (first level)
|
|
166
|
+
*
|
|
167
|
+
* Resolves fragment spreads (via `info.fragments`) and inline fragments,
|
|
168
|
+
* and merges the selections of every field node for the current field, so
|
|
169
|
+
* fields hidden behind `...SomeFragment` or `... on Type { ... }` are
|
|
170
|
+
* reported. Fragment cycles are tolerated. Returned names are the schema
|
|
171
|
+
* field names (not client aliases), deduplicated.
|
|
128
172
|
*/
|
|
129
173
|
getSelectedFields(): string[];
|
|
130
174
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../src/execution-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../src/execution-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAoB,MAAM,SAAS,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB;IAC5B,oDAAoD;;IAEpD,4DAA4D;;IAE5D,yDAAyD;;IAEzD,8DAA8D;;CAEtD,CAAC;AAEX;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,mBAAmB;IAGV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IAEjC,OAAO;IAIP;;OAEG;WACW,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,mBAAmB;IAIpE;;;;;OAKG;IACI,UAAU,CAAC,CAAC,GAAG,UAAU,KAAK,CAAC;IAItC;;;;;OAKG;IACI,OAAO,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC;IAIhC;;;;;OAKG;IACI,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC;IAIhD;;;;;OAKG;IACI,OAAO,CAAC,CAAC,GAAG,kBAAkB,KAAK,CAAC;IAI3C;;OAEG;IACI,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAKvD;;OAEG;IACI,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAKnE;;OAEG;IACI,OAAO,IAAI,aAAa;IAI/B;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;OAEG;IACI,SAAS,IAAI,OAAO;IAI3B;;OAEG;IACI,UAAU,IAAI,QAAQ,GAAG,SAAS;IAIzC;;OAEG;IACI,QAAQ,IAAI,QAAQ,GAAG,SAAS;IAIvC;;OAEG;IACI,YAAY,IAAI,MAAM,GAAG,SAAS;IAKzC;;OAEG;IACI,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAK9C;;OAEG;IACI,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAK9C;;OAEG;IACI,gBAAgB,IAAI,MAAM,GAAG,SAAS;IAK7C;;OAEG;IACI,gBAAgB,IAAI,OAAO,GAAG,UAAU,GAAG,cAAc,GAAG,SAAS;IAK5E;;OAEG;IACI,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC;IAKrD;;;;;;;;;;OAUG;IACI,OAAO,IAAI,MAAM,EAAE;IAM1B;;;;;;OAMG;IACI,WAAW,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IAe5C;;;;;;;;OAQG;IACI,iBAAiB,IAAI,MAAM,EAAE;CA8BrC;AAoBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,UAAU,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,CAE1E;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,CAEpF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAG,kBAAkB,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,CAE/E;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,CAEpE"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @leaven-graphql/nestjs - `@nestjs/graphql` driver backed by Leaven
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2026 Joseph Quinn
|
|
5
|
+
* Licensed under the Apache License, Version 2.0
|
|
6
|
+
*/
|
|
7
|
+
import { AbstractGraphQLDriver } from '@nestjs/graphql';
|
|
8
|
+
import type { GqlModuleOptions } from '@nestjs/graphql';
|
|
9
|
+
import type { GraphQLFormattedError } from 'graphql';
|
|
10
|
+
import { LeavenExecutor, type ExecutorConfig } from '@leaven-graphql/core';
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for {@link LeavenGraphQLDriver}.
|
|
13
|
+
*
|
|
14
|
+
* Everything `@nestjs/graphql` understands is accepted unchanged — including
|
|
15
|
+
* `autoSchemaFile`, `typeDefs`, `path`, `context` and `useGlobalPrefix` — and
|
|
16
|
+
* the Leaven-specific options below are forwarded to the executor that serves
|
|
17
|
+
* the finished schema.
|
|
18
|
+
*/
|
|
19
|
+
export interface LeavenDriverConfig extends GqlModuleOptions {
|
|
20
|
+
/** Document/validation cache configuration passed to the executor. */
|
|
21
|
+
cache?: ExecutorConfig['cache'];
|
|
22
|
+
/** Collect per-operation metrics and expose them under `extensions.metrics`. */
|
|
23
|
+
metrics?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Maximum allowed query complexity. Enforced by the executor itself, which
|
|
26
|
+
* rejects an over-budget document before any resolver runs.
|
|
27
|
+
*/
|
|
28
|
+
maxComplexity?: number;
|
|
29
|
+
/** Maximum allowed query depth. Enforced by the executor at parse time. */
|
|
30
|
+
maxDepth?: number;
|
|
31
|
+
/** Serve GraphiQL on `GET <path>`. Defaults to `false`. */
|
|
32
|
+
playground?: boolean;
|
|
33
|
+
/** WebSocket endpoint advertised to GraphiQL, when subscriptions are served. */
|
|
34
|
+
subscriptionEndpoint?: string;
|
|
35
|
+
/** Final transformation applied to every error in the response. */
|
|
36
|
+
formatError?: (error: GraphQLFormattedError) => GraphQLFormattedError;
|
|
37
|
+
/**
|
|
38
|
+
* Include stack traces in error responses. Setting this also disables
|
|
39
|
+
* production masking, since a masked error carries no stack to include.
|
|
40
|
+
*/
|
|
41
|
+
includeStacktraceInErrorResponses?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A `@nestjs/graphql` driver that executes with Leaven.
|
|
45
|
+
*
|
|
46
|
+
* ## Why this class is the bridge
|
|
47
|
+
*
|
|
48
|
+
* `GraphQLModule.onModuleInit` calls `generateSchema()` before `start()`.
|
|
49
|
+
* `generateSchema()` is inherited from {@link AbstractGraphQLDriver} and
|
|
50
|
+
* delegates to `GraphQLFactory`, which builds the schema from your
|
|
51
|
+
* `@Resolver()` classes with NestJS's `ExternalContextCreator` wrapped around
|
|
52
|
+
* every field resolver. That wrapper is what runs guards, interceptors, pipes,
|
|
53
|
+
* filters and parameter decorators. By the time `start()` receives
|
|
54
|
+
* `options.schema`, the NestJS execution pipeline is already baked into it —
|
|
55
|
+
* this driver only has to execute that schema and serve it over HTTP.
|
|
56
|
+
*
|
|
57
|
+
* This is the opposite of `LeavenModule.forRoot({ typeDefs, resolvers })`,
|
|
58
|
+
* where resolvers are plain functions invoked directly by graphql-js and the
|
|
59
|
+
* NestJS pipeline never runs.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* @Module({
|
|
64
|
+
* imports: [
|
|
65
|
+
* GraphQLModule.forRoot<LeavenDriverConfig>({
|
|
66
|
+
* driver: LeavenGraphQLDriver,
|
|
67
|
+
* autoSchemaFile: true,
|
|
68
|
+
* playground: true,
|
|
69
|
+
* cache: { maxSize: 1000 },
|
|
70
|
+
* }),
|
|
71
|
+
* ],
|
|
72
|
+
* })
|
|
73
|
+
* export class AppModule {}
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare class LeavenGraphQLDriver extends AbstractGraphQLDriver<LeavenDriverConfig> {
|
|
77
|
+
private executor;
|
|
78
|
+
private options;
|
|
79
|
+
/**
|
|
80
|
+
* Serve the schema `@nestjs/graphql` has already built.
|
|
81
|
+
*
|
|
82
|
+
* Registers a POST handler for GraphQL operations and, when `playground` is
|
|
83
|
+
* enabled, a GET handler rendering GraphiQL — both at the module's
|
|
84
|
+
* normalized path.
|
|
85
|
+
*/
|
|
86
|
+
start(options: LeavenDriverConfig): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Release the executor.
|
|
89
|
+
*
|
|
90
|
+
* `clearCaches()` is genuinely asynchronous for a remote cache (a Redis
|
|
91
|
+
* `FLUSH`), so it is awaited: dropping the promise would leave the clear
|
|
92
|
+
* unfinished and turn a rejecting cache into an unhandled rejection during
|
|
93
|
+
* shutdown.
|
|
94
|
+
*/
|
|
95
|
+
stop(): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* The executor currently serving requests, or `null` before `start()` and
|
|
98
|
+
* after `stop()`.
|
|
99
|
+
*/
|
|
100
|
+
getExecutor(): LeavenExecutor | null;
|
|
101
|
+
/**
|
|
102
|
+
* Execute one GraphQL operation and write the HTTP response.
|
|
103
|
+
*/
|
|
104
|
+
private handleOperation;
|
|
105
|
+
/**
|
|
106
|
+
* Render GraphiQL.
|
|
107
|
+
*/
|
|
108
|
+
private servePlayground;
|
|
109
|
+
/**
|
|
110
|
+
* Build the GraphQL context for one request.
|
|
111
|
+
*
|
|
112
|
+
* `req`/`res` are always present so guards and resolvers can reach the
|
|
113
|
+
* transport objects (this is what makes `ctx.req.user` work under Passport);
|
|
114
|
+
* a configured `context` factory or object is merged over them.
|
|
115
|
+
*/
|
|
116
|
+
private buildContext;
|
|
117
|
+
/**
|
|
118
|
+
* Write a GraphQL response with the status the error-handling conventions
|
|
119
|
+
* require.
|
|
120
|
+
*
|
|
121
|
+
* A response carrying `data` is a spec-conformant partial success and stays
|
|
122
|
+
* 200 whatever the error codes say — anything else makes clients treat it as
|
|
123
|
+
* a network failure and discard data they were given. Only a total failure
|
|
124
|
+
* derives its status from the first error's `ErrorCode`, via the
|
|
125
|
+
* `ERROR_CODES` registry that `@leaven-graphql/http` uses.
|
|
126
|
+
*/
|
|
127
|
+
private respond;
|
|
128
|
+
/**
|
|
129
|
+
* Guarantee an `ErrorCode`, apply production masking, then hand the result
|
|
130
|
+
* to a configured `formatError`.
|
|
131
|
+
*
|
|
132
|
+
* By this point {@link formatExecutionError} has already given every NestJS
|
|
133
|
+
* `HttpException` its `ErrorCode`; what is left here is the residue — a
|
|
134
|
+
* resolver that threw a plain `Error` — which must still be coded and, in
|
|
135
|
+
* production, masked.
|
|
136
|
+
*
|
|
137
|
+
* The executor reports errors as already-serialised `GraphQLFormattedError`s
|
|
138
|
+
* (`GraphQLError.toJSON()`), so `formatError` from `@leaven-graphql/errors`
|
|
139
|
+
* — whose input is an `Error`/`GraphQLError` — cannot be reused verbatim
|
|
140
|
+
* here; this reproduces its masking rule for the formatted shape. Errors
|
|
141
|
+
* *thrown* out of the executor do go through it, in
|
|
142
|
+
* {@link formatThrownError}.
|
|
143
|
+
*/
|
|
144
|
+
private finalizeError;
|
|
145
|
+
/**
|
|
146
|
+
* Turn an error thrown out of the executor into a response error.
|
|
147
|
+
*
|
|
148
|
+
* Leaven errors keep their code, extensions and message. Unexpected errors
|
|
149
|
+
* are masked only in production — masking them in development would hide the
|
|
150
|
+
* real message from the developer — and `includeStacktraceInErrorResponses`
|
|
151
|
+
* suppresses masking outright, since a masked error carries no stack to
|
|
152
|
+
* include. This matches `LeavenDriver.execute`.
|
|
153
|
+
*/
|
|
154
|
+
private formatThrownError;
|
|
155
|
+
/**
|
|
156
|
+
* Read the operation from the request.
|
|
157
|
+
*
|
|
158
|
+
* The adapter may hand over an already-parsed body, a raw string, or nothing
|
|
159
|
+
* at all when no body-parser is installed; the raw Fetch `Request` is the
|
|
160
|
+
* last resort.
|
|
161
|
+
*/
|
|
162
|
+
private static readBody;
|
|
163
|
+
/**
|
|
164
|
+
* Serialize one execution error, translating a NestJS `HttpException` into
|
|
165
|
+
* the equivalent {@link ErrorCode}.
|
|
166
|
+
*
|
|
167
|
+
* Installed as the executor's `formatExecutionError` hook, which runs while
|
|
168
|
+
* the graphql-js `GraphQLError` is still intact — the only point at which
|
|
169
|
+
* `originalError` is still reachable. After `toJSON()` it is gone, and an
|
|
170
|
+
* `HttpException` (unlike a `LeavenError`) has no `extensions` of its own
|
|
171
|
+
* for graphql-js to adopt, so the error would otherwise reach the client as
|
|
172
|
+
* a bare `INTERNAL_ERROR` with a 500: a guard's `ForbiddenException` would be
|
|
173
|
+
* indistinguishable from a crash.
|
|
174
|
+
*
|
|
175
|
+
* `message`, `path` and `locations` are preserved exactly as serialized;
|
|
176
|
+
* only `extensions.code` is supplied.
|
|
177
|
+
*/
|
|
178
|
+
private static formatExecutionError;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=graphql-driver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-driver.d.ts","sourceRoot":"","sources":["../src/graphql-driver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAgB,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA8C3E;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,sEAAsE;IACtE,KAAK,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAChC,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gFAAgF;IAChF,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mEAAmE;IACnE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,qBAAqB,CAAC;IACtE;;;OAGG;IACH,iCAAiC,CAAC,EAAE,OAAO,CAAC;CAC7C;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB,CAAC,kBAAkB,CAAC;IAChF,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAAmC;IAElD;;;;;;OAMG;IACU,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgC9D;;;;;;;OAOG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOlC;;;OAGG;IACI,WAAW,IAAI,cAAc,GAAG,IAAI;IAI3C;;OAEG;YACW,eAAe;IAsE7B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;;;;;OAMG;YACW,YAAY;IAqB1B;;;;;;;;;OASG;IACH,OAAO,CAAC,OAAO;IAiCf;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,aAAa;IA2BrB;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IAezB;;;;;;OAMG;mBACkB,QAAQ;IAmB7B;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;CAoBpC"}
|