@fluojs/graphql 1.1.0 → 2.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-of-patch.d.ts","sourceRoot":"","sources":["../src/instance-of-patch.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG;IAC1C,QAAQ,CAAC,SAAS,EAAE;QAClB,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC;KACxC,CAAC;CACH,CAAC;AAEF,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,kBAAkB,KAAK,OAAO,CAAC;AAE7F,iFAAiF;AACjF,MAAM,MAAM,uBAAuB,GAAG;IACpC,UAAU,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AA0IF;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,uBAAuB,EACzC,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,GAC9B,MAAM,IAAI,CAmCZ"}
@@ -0,0 +1,126 @@
1
+ /** Represents a GraphQL constructor inspected by the private `instanceOf` helper. */
2
+
3
+ /** Represents the private GraphQL `instanceOf` helper signature. */
4
+
5
+ /** Represents the mutable private GraphQL module object that owns the helper. */
6
+
7
+ const graphqlInstanceOfPatchStateRegistryKey = Symbol.for('@fluojs/graphql.instance-of-patch-state-registry/v1');
8
+ function isGraphqlInstanceOfPatchStateRegistry(value) {
9
+ if (typeof value !== 'object' || value === null) {
10
+ return false;
11
+ }
12
+ return Reflect.get(value, 'states') instanceof WeakMap;
13
+ }
14
+ function getGraphqlInstanceOfPatchStates() {
15
+ const registeredValue = Reflect.get(globalThis, graphqlInstanceOfPatchStateRegistryKey);
16
+ if (isGraphqlInstanceOfPatchStateRegistry(registeredValue)) {
17
+ return registeredValue.states;
18
+ }
19
+ const registry = {
20
+ states: new WeakMap()
21
+ };
22
+ Object.defineProperty(globalThis, graphqlInstanceOfPatchStateRegistryKey, {
23
+ configurable: false,
24
+ enumerable: false,
25
+ value: registry,
26
+ writable: false
27
+ });
28
+ return registry.states;
29
+ }
30
+ function createGraphqlInstanceOfPatchState(originalInstanceOf) {
31
+ let patchState;
32
+ const allowedObjectSets = new Set();
33
+ const patchedInstanceOf = (value, constructor) => {
34
+ if (patchState.isEvaluatingDelegate) {
35
+ return isAllowedCrossRealmGraphqlObject(value, constructor, allowedObjectSets);
36
+ }
37
+ patchState.isEvaluatingDelegate = true;
38
+ try {
39
+ try {
40
+ if (patchState.originalInstanceOf(value, constructor)) {
41
+ return true;
42
+ }
43
+ } catch (error) {
44
+ if (isAllowedCrossRealmGraphqlObject(value, constructor, allowedObjectSets)) {
45
+ return true;
46
+ }
47
+ throw error;
48
+ }
49
+ return isAllowedCrossRealmGraphqlObject(value, constructor, allowedObjectSets);
50
+ } finally {
51
+ patchState.isEvaluatingDelegate = false;
52
+ }
53
+ };
54
+ patchState = {
55
+ allowedObjectSets,
56
+ isEvaluatingDelegate: false,
57
+ originalInstanceOf,
58
+ patchedInstanceOf
59
+ };
60
+ return patchState;
61
+ }
62
+ function getCrossRealmGraphqlTag(value, constructor) {
63
+ const prototypeTag = constructor.prototype?.[Symbol.toStringTag];
64
+ const className = typeof prototypeTag === 'string' ? prototypeTag : constructor.name;
65
+ if (typeof className !== 'string' || !className.startsWith('GraphQL')) {
66
+ return undefined;
67
+ }
68
+ if (typeof value !== 'object' || value === null) {
69
+ return undefined;
70
+ }
71
+ const valueTag = Reflect.get(value, Symbol.toStringTag);
72
+ if (typeof valueTag === 'string') {
73
+ return valueTag === className ? className : undefined;
74
+ }
75
+ const valueConstructor = Reflect.get(value, 'constructor');
76
+ const valueClassName = typeof valueConstructor === 'object' && valueConstructor !== null || typeof valueConstructor === 'function' ? Reflect.get(valueConstructor, 'name') : undefined;
77
+ return typeof valueClassName === 'string' && valueClassName === className ? className : undefined;
78
+ }
79
+ function isAllowedCrossRealmGraphqlObject(value, constructor, allowedObjectSets) {
80
+ if (typeof value !== 'object' || value === null) {
81
+ return false;
82
+ }
83
+ for (const allowedObjects of allowedObjectSets) {
84
+ if (allowedObjects.has(value)) {
85
+ return getCrossRealmGraphqlTag(value, constructor) !== undefined;
86
+ }
87
+ }
88
+ return false;
89
+ }
90
+
91
+ /**
92
+ * Installs and releases a cross-realm GraphQL `instanceOf` patch for one module object.
93
+ *
94
+ * @param instanceOfModule The GraphQL module object that owns the `instanceOf` helper.
95
+ * @param allowedObjects The active application's cross-realm GraphQL object allowlist.
96
+ * @returns A one-time release callback for the application's allowlist.
97
+ */
98
+ export function installGraphqlInstanceOfPatch(instanceOfModule, allowedObjects) {
99
+ const patchStates = getGraphqlInstanceOfPatchStates();
100
+ let patchState = patchStates.get(instanceOfModule);
101
+ if (patchState === undefined) {
102
+ patchState = createGraphqlInstanceOfPatchState(instanceOfModule.instanceOf);
103
+ patchStates.set(instanceOfModule, patchState);
104
+ } else if (instanceOfModule.instanceOf !== patchState.patchedInstanceOf) {
105
+ patchState.originalInstanceOf = instanceOfModule.instanceOf;
106
+ }
107
+ instanceOfModule.instanceOf = patchState.patchedInstanceOf;
108
+ patchState.allowedObjectSets.add(allowedObjects);
109
+ let released = false;
110
+ return () => {
111
+ if (released) {
112
+ return;
113
+ }
114
+ released = true;
115
+ patchState.allowedObjectSets.delete(allowedObjects);
116
+ if (patchState.allowedObjectSets.size > 0) {
117
+ return;
118
+ }
119
+ if (instanceOfModule.instanceOf === patchState.patchedInstanceOf) {
120
+ instanceOfModule.instanceOf = patchState.originalInstanceOf;
121
+ }
122
+ if (patchStates.get(instanceOfModule) === patchState) {
123
+ patchStates.delete(instanceOfModule);
124
+ }
125
+ };
126
+ }
package/dist/module.d.ts CHANGED
@@ -1,14 +1,6 @@
1
- import type { Provider } from '@fluojs/di';
1
+ import type { InjectionToken } from '@fluojs/core';
2
2
  import { type ModuleType } from '@fluojs/runtime';
3
- import type { GraphqlModuleOptions } from './types.js';
4
- /**
5
- * Creates GraphQL runtime providers for module-level options and lifecycle wiring.
6
- *
7
- * @param options GraphQL module options used by the lifecycle service and endpoint controller.
8
- * @returns Provider definitions that register only the internal options token and GraphQL lifecycle service; this helper
9
- * does not register `GraphqlEndpointController` or mount the `/graphql` endpoint by itself.
10
- */
11
- export declare function createGraphqlProviders(options: GraphqlModuleOptions): Provider[];
3
+ import type { GraphqlAsyncModuleOptions, GraphqlModuleOptions } from './types.js';
12
4
  /**
13
5
  * Represents the graphql module.
14
6
  */
@@ -21,5 +13,17 @@ export declare class GraphqlModule {
21
13
  * module path (not `createGraphqlProviders(...)` alone) when the application should expose `/graphql`.
22
14
  */
23
15
  static forRoot(options?: GraphqlModuleOptions): ModuleType;
16
+ /**
17
+ * Registers GraphQL from options resolved by explicitly injected application dependencies.
18
+ *
19
+ * The factory runs once in each application context. Only `inject` and `useFactory` are
20
+ * supported; NestJS-style `imports`, `useClass`, `useExisting`, and implicit discovery are rejected.
21
+ *
22
+ * @param options Injected dependency tokens and the factory that resolves GraphQL options.
23
+ * @returns A module definition that resolves GraphQL configuration before endpoint lifecycle wiring begins.
24
+ *
25
+ * @throws {TypeError} When the options shape requests unsupported registration behavior.
26
+ */
27
+ static forRootAsync<const TTokens extends readonly InjectionToken[]>(options: GraphqlAsyncModuleOptions<TTokens>): ModuleType;
24
28
  }
25
29
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,QAAQ,EAAE,CAQhF;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,oBAAyB,GAAG,UAAU;CAS/D"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAiDlF;;GAEG;AACH,qBAAa,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,oBAAyB,GAAG,UAAU;IAa9D;;;;;;;;;;OAUG;IACH,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,SAAS,SAAS,cAAc,EAAE,EACjE,OAAO,EAAE,yBAAyB,CAAC,OAAO,CAAC,GAC1C,UAAU;CA4Bd"}
package/dist/module.js CHANGED
@@ -1,18 +1,37 @@
1
+ import { isForwardRef, isOptionalToken } from '@fluojs/di';
1
2
  import { defineModule } from '@fluojs/runtime';
3
+ import { RUNTIME_CONTAINER } from '@fluojs/runtime/internal';
2
4
  import { GRAPHQL_INTERNAL_MODULE_OPTIONS_TOKEN } from './internal-tokens.js';
3
5
  import { GraphqlEndpointController, GraphqlLifecycleService } from './service.js';
4
- /**
5
- * Creates GraphQL runtime providers for module-level options and lifecycle wiring.
6
- *
7
- * @param options GraphQL module options used by the lifecycle service and endpoint controller.
8
- * @returns Provider definitions that register only the internal options token and GraphQL lifecycle service; this helper
9
- * does not register `GraphqlEndpointController` or mount the `/graphql` endpoint by itself.
10
- */
11
- export function createGraphqlProviders(options) {
12
- return [{
13
- provide: GRAPHQL_INTERNAL_MODULE_OPTIONS_TOKEN,
14
- useValue: options
15
- }, GraphqlLifecycleService];
6
+ function createGraphqlProviders(optionsProvider) {
7
+ return [optionsProvider, GraphqlLifecycleService];
8
+ }
9
+ function assertGraphqlAsyncModuleOptions(options) {
10
+ if (options === null || typeof options !== 'object') {
11
+ throw new TypeError('GraphqlModule.forRootAsync requires an options object.');
12
+ }
13
+ const unsupportedKey = Object.keys(options).find(key => key !== 'inject' && key !== 'useFactory');
14
+ if (unsupportedKey !== undefined) {
15
+ throw new TypeError(`GraphqlModule.forRootAsync does not support "${unsupportedKey}"; use only inject and useFactory.`);
16
+ }
17
+ if (typeof options.useFactory !== 'function') {
18
+ throw new TypeError('GraphqlModule.forRootAsync requires a useFactory function.');
19
+ }
20
+ if (options.inject !== undefined && !Array.isArray(options.inject)) {
21
+ throw new TypeError('GraphqlModule.forRootAsync inject must be an array of application tokens.');
22
+ }
23
+ }
24
+ function isContainer(value) {
25
+ return value !== null && typeof value === 'object' && 'has' in value && 'resolve' in value;
26
+ }
27
+ async function resolveAsyncDependency(container, token) {
28
+ if (isForwardRef(token)) {
29
+ return await container.resolve(token.forwardRef());
30
+ }
31
+ if (isOptionalToken(token)) {
32
+ return container.has(token.token) ? await container.resolve(token.token) : undefined;
33
+ }
34
+ return await container.resolve(token);
16
35
  }
17
36
 
18
37
  /**
@@ -30,8 +49,44 @@ export class GraphqlModule {
30
49
  class GraphqlRootModule extends GraphqlModule {}
31
50
  return defineModule(GraphqlRootModule, {
32
51
  controllers: [GraphqlEndpointController],
33
- middleware: [],
34
- providers: createGraphqlProviders(options)
52
+ middleware: [GraphqlLifecycleService],
53
+ providers: createGraphqlProviders({
54
+ provide: GRAPHQL_INTERNAL_MODULE_OPTIONS_TOKEN,
55
+ useValue: options
56
+ })
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Registers GraphQL from options resolved by explicitly injected application dependencies.
62
+ *
63
+ * The factory runs once in each application context. Only `inject` and `useFactory` are
64
+ * supported; NestJS-style `imports`, `useClass`, `useExisting`, and implicit discovery are rejected.
65
+ *
66
+ * @param options Injected dependency tokens and the factory that resolves GraphQL options.
67
+ * @returns A module definition that resolves GraphQL configuration before endpoint lifecycle wiring begins.
68
+ *
69
+ * @throws {TypeError} When the options shape requests unsupported registration behavior.
70
+ */
71
+ static forRootAsync(options) {
72
+ assertGraphqlAsyncModuleOptions(options);
73
+ class GraphqlAsyncRootModule extends GraphqlModule {}
74
+ return defineModule(GraphqlAsyncRootModule, {
75
+ controllers: [GraphqlEndpointController],
76
+ middleware: [GraphqlLifecycleService],
77
+ providers: createGraphqlProviders({
78
+ inject: [RUNTIME_CONTAINER],
79
+ provide: GRAPHQL_INTERNAL_MODULE_OPTIONS_TOKEN,
80
+ scope: 'singleton',
81
+ useFactory: async (...dependencies) => {
82
+ const [runtimeContainer] = dependencies;
83
+ if (!isContainer(runtimeContainer)) {
84
+ throw new TypeError('GraphqlModule.forRootAsync could not resolve the application container.');
85
+ }
86
+ const injectedDependencies = await Promise.all((options.inject ?? []).map(async token => await resolveAsyncDependency(runtimeContainer, token)));
87
+ return Reflect.apply(options.useFactory, options, injectedDependencies);
88
+ }
89
+ })
35
90
  });
36
91
  }
37
92
  }
@@ -10,6 +10,9 @@ interface GraphqlSubscribePayload {
10
10
  query: string;
11
11
  variables?: Record<string, unknown> | null;
12
12
  }
13
+ /**
14
+ * Describes a GraphQL-over-WebSocket subscription forwarded to application hooks.
15
+ */
13
16
  export interface GraphqlNodeWebSocketSubscribeRequest {
14
17
  connectionParams?: Record<string, unknown>;
15
18
  operationId: string;
@@ -17,7 +20,15 @@ export interface GraphqlNodeWebSocketSubscribeRequest {
17
20
  request: FrameworkRequest;
18
21
  socket: object;
19
22
  }
23
+ /**
24
+ * Represents a registered Node GraphQL WebSocket transport.
25
+ */
20
26
  export interface GraphqlNodeWebSocketTransport {
27
+ /**
28
+ * Releases websocket listeners, clients, and GraphQL protocol resources.
29
+ *
30
+ * @returns A promise that resolves when cleanup completes or rejects with all current cleanup failures.
31
+ */
21
32
  dispose(): Promise<void>;
22
33
  }
23
34
  interface GraphqlNodeWebSocketTransportOptions {
@@ -31,6 +42,12 @@ interface GraphqlNodeWebSocketTransportOptions {
31
42
  onSubscribe: (request: GraphqlNodeWebSocketSubscribeRequest) => Promise<ExecutionArgs | readonly GraphQLErrorType[]>;
32
43
  subscribe: typeof subscribeGraphql;
33
44
  }
45
+ /**
46
+ * Registers GraphQL-over-WebSocket upgrade handling for a Node HTTP/S adapter.
47
+ *
48
+ * @param options Transport dependencies and WebSocket lifecycle hooks.
49
+ * @returns A transport that unregisters upgrade handling and disposes WebSocket clients.
50
+ */
34
51
  export declare function createNodeGraphqlWebSocketTransport(options: GraphqlNodeWebSocketTransportOptions): Promise<GraphqlNodeWebSocketTransport>;
35
52
  export {};
36
53
  //# sourceMappingURL=graphql-websocket-transport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graphql-websocket-transport.d.ts","sourceRoot":"","sources":["../../src/node/graphql-websocket-transport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,KAAK,EACV,aAAa,EACb,OAAO,IAAI,cAAc,EACzB,YAAY,IAAI,gBAAgB,EAChC,SAAS,IAAI,gBAAgB,EAC9B,MAAM,SAAS,CAAC;AAgBjB,UAAU,0BAA0B;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,0BAA0B,EAAE,MAAM,CAAC;IACnC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,uBAAuB;IAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,oCAAoC;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,uBAAuB,CAAC;IACjC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,UAAU,oCAAoC;IAC5C,OAAO,EAAE,sBAAsB,CAAC;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,OAAO,EAAE,OAAO,cAAc,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7E,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1D,WAAW,EAAE,CAAC,OAAO,EAAE,oCAAoC,KAAK,OAAO,CAAC,aAAa,GAAG,SAAS,gBAAgB,EAAE,CAAC,CAAC;IACrH,SAAS,EAAE,OAAO,gBAAgB,CAAC;CACpC;AA2HD,wBAAsB,mCAAmC,CACvD,OAAO,EAAE,oCAAoC,GAC5C,OAAO,CAAC,6BAA6B,CAAC,CAsFxC"}
1
+ {"version":3,"file":"graphql-websocket-transport.d.ts","sourceRoot":"","sources":["../../src/node/graphql-websocket-transport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,KAAK,EACV,aAAa,EACb,OAAO,IAAI,cAAc,EACzB,YAAY,IAAI,gBAAgB,EAChC,SAAS,IAAI,gBAAgB,EAC9B,MAAM,SAAS,CAAC;AAgBjB,UAAU,0BAA0B;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,0BAA0B,EAAE,MAAM,CAAC;IACnC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,uBAAuB;IAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,uBAAuB,CAAC;IACjC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,UAAU,oCAAoC;IAC5C,OAAO,EAAE,sBAAsB,CAAC;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,OAAO,EAAE,OAAO,cAAc,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7E,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1D,WAAW,EAAE,CAAC,OAAO,EAAE,oCAAoC,KAAK,OAAO,CAAC,aAAa,GAAG,SAAS,gBAAgB,EAAE,CAAC,CAAC;IACrH,SAAS,EAAE,OAAO,gBAAgB,CAAC;CACpC;AA2ID;;;;;GAKG;AACH,wBAAsB,mCAAmC,CACvD,OAAO,EAAE,oCAAoC,GAC5C,OAAO,CAAC,6BAA6B,CAAC,CAkIxC"}
@@ -2,6 +2,15 @@ import { handleProtocols } from 'graphql-ws';
2
2
  import { useServer } from 'graphql-ws/lib/use/ws';
3
3
  import { WebSocketServer } from 'ws';
4
4
  import { isGraphqlPath } from '../transport/transport.js';
5
+
6
+ /**
7
+ * Describes a GraphQL-over-WebSocket subscription forwarded to application hooks.
8
+ */
9
+
10
+ /**
11
+ * Represents a registered Node GraphQL WebSocket transport.
12
+ */
13
+
5
14
  function hasNodeUpgradeServer(value) {
6
15
  if (typeof value !== 'object' || value === null) {
7
16
  return false;
@@ -40,6 +49,18 @@ function closeWebSocketServer(server) {
40
49
  });
41
50
  });
42
51
  }
52
+ function collectCleanupError(errors, error) {
53
+ if (error instanceof AggregateError) {
54
+ for (const nested of error.errors) {
55
+ collectCleanupError(errors, nested);
56
+ }
57
+ return;
58
+ }
59
+ if (errors.includes(error)) {
60
+ return;
61
+ }
62
+ errors.push(error);
63
+ }
43
64
  function resolveUpgradeServer(adapter) {
44
65
  if (typeof adapter.getServer !== 'function') {
45
66
  throw new Error('GraphQL websocket subscriptions require an HTTP adapter with getServer(). Use the Node HTTP adapter or provide a compatible adapter implementation.');
@@ -83,17 +104,37 @@ function createSubscribeRequest(context, message) {
83
104
  socket: context.extra.socket
84
105
  };
85
106
  }
107
+
108
+ /**
109
+ * Registers GraphQL-over-WebSocket upgrade handling for a Node HTTP/S adapter.
110
+ *
111
+ * @param options Transport dependencies and WebSocket lifecycle hooks.
112
+ * @returns A transport that unregisters upgrade handling and disposes WebSocket clients.
113
+ */
86
114
  export async function createNodeGraphqlWebSocketTransport(options) {
87
115
  const upgradeServer = resolveUpgradeServer(options.adapter);
88
116
  const disconnectErrors = [];
117
+ const disconnectedSockets = new Set();
89
118
  const pendingDisconnects = new Set();
119
+ let websocketDisposableDisposed = false;
120
+ let websocketServerClosed = false;
121
+ let inFlightDispose;
90
122
  const websocketServer = new WebSocketServer({
91
123
  handleProtocols: protocols => handleProtocols(protocols),
92
124
  maxPayload: options.limits?.maxPayloadBytes ?? 0,
93
125
  noServer: true
94
126
  });
95
127
  const upgradeListener = createUpgradeListener(websocketServer, options.limits);
128
+ const drainDisconnectErrors = cleanupErrors => {
129
+ for (const error of disconnectErrors.splice(0, disconnectErrors.length)) {
130
+ collectCleanupError(cleanupErrors, error);
131
+ }
132
+ };
96
133
  const trackDisconnect = socketKey => {
134
+ if (disconnectedSockets.has(socketKey)) {
135
+ return;
136
+ }
137
+ disconnectedSockets.add(socketKey);
97
138
  const pendingDisconnect = Promise.resolve(options.onDisconnect(socketKey)).catch(error => {
98
139
  disconnectErrors.push(error);
99
140
  }).finally(() => {
@@ -110,47 +151,69 @@ export async function createNodeGraphqlWebSocketTransport(options) {
110
151
  onComplete: async (context, message) => {
111
152
  await options.onComplete(context.extra.socket, message.id);
112
153
  },
113
- onDisconnect: async context => {
114
- await options.onDisconnect(context.extra.socket);
154
+ onDisconnect: context => {
155
+ trackDisconnect(context.extra.socket);
115
156
  },
116
157
  onSubscribe: async (context, message) => options.onSubscribe(createSubscribeRequest(context, message)),
117
158
  subscribe: options.subscribe
118
159
  }, websocketServer, options.keepAliveMs);
119
160
  websocketServer.on('connection', trackConnection);
120
161
  upgradeServer.on('upgrade', upgradeListener);
121
- return {
122
- async dispose() {
123
- let disposeError;
124
- websocketServer.off('connection', trackConnection);
125
- upgradeServer.off('upgrade', upgradeListener);
126
- for (const client of websocketServer.clients) {
127
- client.terminate();
128
- }
129
- const disconnectResults = await Promise.allSettled(pendingDisconnects);
130
- for (const error of disconnectErrors) {
131
- disposeError ??= error;
132
- }
162
+ const runDispose = async () => {
163
+ const cleanupErrors = [];
164
+ drainDisconnectErrors(cleanupErrors);
165
+ websocketServer.off('connection', trackConnection);
166
+ upgradeServer.off('upgrade', upgradeListener);
167
+ for (const client of websocketServer.clients) {
168
+ client.terminate();
169
+ }
170
+ while (pendingDisconnects.size > 0) {
171
+ const disconnectResults = await Promise.allSettled([...pendingDisconnects]);
133
172
  for (const result of disconnectResults) {
134
173
  if (result.status === 'rejected') {
135
- disposeError ??= result.reason;
174
+ collectCleanupError(cleanupErrors, result.reason);
136
175
  }
137
176
  }
177
+ }
178
+ drainDisconnectErrors(cleanupErrors);
179
+ if (!websocketDisposableDisposed) {
138
180
  try {
139
181
  await websocketDisposable.dispose();
182
+ websocketDisposableDisposed = true;
140
183
  } catch (error) {
141
- disposeError = error;
184
+ collectCleanupError(cleanupErrors, error);
142
185
  }
186
+ }
187
+ if (!websocketServerClosed) {
143
188
  try {
144
189
  await closeWebSocketServer(websocketServer);
190
+ websocketServerClosed = true;
145
191
  } catch (error) {
146
- disposeError ??= error;
147
- }
148
- if (disposeError instanceof Error) {
149
- throw disposeError;
192
+ collectCleanupError(cleanupErrors, error);
150
193
  }
151
- if (disposeError !== undefined) {
152
- throw new Error(String(disposeError));
194
+ }
195
+ while (pendingDisconnects.size > 0) {
196
+ const lateDisconnectResults = await Promise.allSettled([...pendingDisconnects]);
197
+ for (const result of lateDisconnectResults) {
198
+ if (result.status === 'rejected') {
199
+ collectCleanupError(cleanupErrors, result.reason);
200
+ }
153
201
  }
154
202
  }
203
+ drainDisconnectErrors(cleanupErrors);
204
+ if (cleanupErrors.length === 1) {
205
+ throw cleanupErrors[0];
206
+ }
207
+ if (cleanupErrors.length > 1) {
208
+ throw new AggregateError(cleanupErrors, 'Failed to dispose GraphQL websocket transport resources.');
209
+ }
210
+ };
211
+ return {
212
+ async dispose() {
213
+ inFlightDispose ??= runDispose().finally(() => {
214
+ inFlightDispose = undefined;
215
+ });
216
+ await inFlightDispose;
217
+ }
155
218
  };
156
219
  }
@@ -1,4 +1,4 @@
1
- import { getDtoValidationSchema } from '@fluojs/core/internal';
1
+ import { getDtoValidationSchema } from '@fluojs/core/request-pipeline';
2
2
  import { DefaultValidator } from '@fluojs/validation';
3
3
  import { isGraphqlListTypeRef } from '../types.js';
4
4
  const defaultValidator = new DefaultValidator();
@@ -1,7 +1,8 @@
1
- import type { GraphQLFieldConfigMap, GraphQLOutputType } from 'graphql';
1
+ import type { GraphQLFieldConfigArgumentMap, GraphQLFieldConfigMap, GraphQLOutputType } from 'graphql';
2
2
  import type { GraphQLContext as FluoGraphQLContext, GraphqlRootOutputType, ResolverDescriptor, ResolverHandlerDescriptor } from '../types.js';
3
3
  type ResolveOutputType = (outputType: GraphqlRootOutputType) => GraphQLOutputType;
4
- type InvokeObjectFieldResolver = (descriptor: ResolverDescriptor, handler: ResolverHandlerDescriptor, source: unknown, contextValue: FluoGraphQLContext) => Promise<unknown>;
4
+ type ResolveInputArgs = (handler: ResolverHandlerDescriptor) => GraphQLFieldConfigArgumentMap;
5
+ type InvokeObjectFieldResolver = (descriptor: ResolverDescriptor, handler: ResolverHandlerDescriptor, args: Record<string, unknown>, source: unknown, contextValue: FluoGraphQLContext) => Promise<unknown>;
5
6
  /**
6
7
  * Indexes object field resolver descriptors and attaches them to matching code-first object types.
7
8
  */
@@ -9,8 +10,8 @@ export declare class ObjectFieldResolverRegistry {
9
10
  private readonly attachedTypeNames;
10
11
  private readonly entriesByTypeName;
11
12
  constructor(descriptors: readonly ResolverDescriptor[]);
12
- attach(typeName: string, fields: GraphQLFieldConfigMap<unknown, FluoGraphQLContext>, resolveOutputType: ResolveOutputType, invokeResolver: InvokeObjectFieldResolver): GraphQLFieldConfigMap<unknown, FluoGraphQLContext>;
13
- createMethodArguments(handler: ResolverHandlerDescriptor, parent: unknown, contextValue: FluoGraphQLContext): unknown[];
13
+ attach(typeName: string, fields: GraphQLFieldConfigMap<unknown, FluoGraphQLContext>, resolveOutputType: ResolveOutputType, resolveInputArgs: ResolveInputArgs, invokeResolver: InvokeObjectFieldResolver): GraphQLFieldConfigMap<unknown, FluoGraphQLContext>;
14
+ createMethodArguments(handler: ResolverHandlerDescriptor, input: unknown, parent: unknown, contextValue: FluoGraphQLContext): unknown[];
14
15
  assertAllTargetsAttached(): void;
15
16
  }
16
17
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"object-field-resolvers.d.ts","sourceRoot":"","sources":["../../src/schema/object-field-resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAExE,OAAO,KAAK,EACV,cAAc,IAAI,kBAAkB,EACpC,qBAAqB,EACrB,kBAAkB,EAClB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAOrB,KAAK,iBAAiB,GAAG,CAAC,UAAU,EAAE,qBAAqB,KAAK,iBAAiB,CAAC;AAElF,KAAK,yBAAyB,GAAG,CAC/B,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,yBAAyB,EAClC,MAAM,EAAE,OAAO,EACf,YAAY,EAAE,kBAAkB,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;AAMtB;;GAEG;AACH,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA4D;gBAElF,WAAW,EAAE,SAAS,kBAAkB,EAAE;IAqBtD,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,CAAC,EAC1D,iBAAiB,EAAE,iBAAiB,EACpC,cAAc,EAAE,yBAAyB,GACxC,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,CAAC;IA+BrD,qBAAqB,CACnB,OAAO,EAAE,yBAAyB,EAClC,MAAM,EAAE,OAAO,EACf,YAAY,EAAE,kBAAkB,GAC/B,OAAO,EAAE;IAoBZ,wBAAwB,IAAI,IAAI;CASjC"}
1
+ {"version":3,"file":"object-field-resolvers.d.ts","sourceRoot":"","sources":["../../src/schema/object-field-resolvers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,6BAA6B,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEvG,OAAO,KAAK,EACV,cAAc,IAAI,kBAAkB,EACpC,qBAAqB,EACrB,kBAAkB,EAClB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAOrB,KAAK,iBAAiB,GAAG,CAAC,UAAU,EAAE,qBAAqB,KAAK,iBAAiB,CAAC;AAClF,KAAK,gBAAgB,GAAG,CAAC,OAAO,EAAE,yBAAyB,KAAK,6BAA6B,CAAC;AAE9F,KAAK,yBAAyB,GAAG,CAC/B,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,yBAAyB,EAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,OAAO,EACf,YAAY,EAAE,kBAAkB,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;AAMtB;;GAEG;AACH,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA4D;gBAElF,WAAW,EAAE,SAAS,kBAAkB,EAAE;IAqBtD,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,CAAC,EAC1D,iBAAiB,EAAE,iBAAiB,EACpC,gBAAgB,EAAE,gBAAgB,EAClC,cAAc,EAAE,yBAAyB,GACxC,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,CAAC;IAmCrD,qBAAqB,CACnB,OAAO,EAAE,yBAAyB,EAClC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,YAAY,EAAE,kBAAkB,GAC/B,OAAO,EAAE;IAuBZ,wBAAwB,IAAI,IAAI;CASjC"}
@@ -1,3 +1,4 @@
1
+ import { GraphQLNonNull } from 'graphql';
1
2
  function assertNever(value) {
2
3
  throw new Error(`Unsupported GraphQL field resolver parameter binding: ${String(value)}`);
3
4
  }
@@ -26,7 +27,7 @@ export class ObjectFieldResolverRegistry {
26
27
  }
27
28
  }
28
29
  }
29
- attach(typeName, fields, resolveOutputType, invokeResolver) {
30
+ attach(typeName, fields, resolveOutputType, resolveInputArgs, invokeResolver) {
30
31
  const resolverFields = this.entriesByTypeName.get(typeName);
31
32
  if (!resolverFields) {
32
33
  return fields;
@@ -41,21 +42,28 @@ export class ObjectFieldResolverRegistry {
41
42
  if (!outputType) {
42
43
  throw new Error(`GraphQL object field resolver "${typeName}.${fieldName}" must target an existing field or declare a type.`);
43
44
  }
45
+ const hasExplicitNonNullableNewField = existingField === undefined && entry.handler.outputType !== undefined && entry.handler.nullable === false;
44
46
  attachedFields[fieldName] = {
45
47
  ...existingField,
46
- resolve: async (source, _args, contextValue) => invokeResolver(entry.descriptor, entry.handler, source, contextValue),
47
- type: outputType
48
+ ...(entry.handler.inputClass ? {
49
+ args: resolveInputArgs(entry.handler)
50
+ } : {}),
51
+ resolve: async (source, args, contextValue) => invokeResolver(entry.descriptor, entry.handler, args, source, contextValue),
52
+ type: hasExplicitNonNullableNewField ? new GraphQLNonNull(outputType) : outputType
48
53
  };
49
54
  }
50
55
  return attachedFields;
51
56
  }
52
- createMethodArguments(handler, parent, contextValue) {
57
+ createMethodArguments(handler, input, parent, contextValue) {
53
58
  const lastBinding = handler.parameterBindings.at(-1);
54
59
  const methodArguments = Array.from({
55
60
  length: (lastBinding?.index ?? -1) + 1
56
61
  });
57
62
  for (const binding of handler.parameterBindings) {
58
63
  switch (binding.kind) {
64
+ case 'input':
65
+ methodArguments[binding.index] = input;
66
+ break;
59
67
  case 'parent':
60
68
  methodArguments[binding.index] = parent;
61
69
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAEV,YAAY,IAAI,gBAAgB,EAMhC,WAAW,IAAI,eAAe,EAC9B,cAAc,IAAI,kBAAkB,EACpC,iBAAiB,IAAI,qBAAqB,EAE1C,iBAAiB,EACjB,aAAa,IAAI,iBAAiB,EAClC,gBAAgB,IAAI,oBAAoB,EACzC,MAAM,SAAS,CAAC;AAGjB,OAAO,EAOL,KAAK,kBAAkB,EAGxB,MAAM,aAAa,CAAC;AAGrB,KAAK,eAAe,GAAG;IACrB,YAAY,EAAE,OAAO,gBAAgB,CAAC;IACtC,cAAc,EAAE,iBAAiB,CAAC;IAClC,YAAY,EAAE,iBAAiB,CAAC;IAChC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,WAAW,EAAE,OAAO,eAAe,CAAC;IACpC,cAAc,EAAE,OAAO,kBAAkB,CAAC;IAC1C,iBAAiB,EAAE,OAAO,qBAAqB,CAAC;IAChD,aAAa,EAAE,OAAO,iBAAiB,CAAC;IACxC,aAAa,EAAE,iBAAiB,CAAC;IACjC,gBAAgB,EAAE,OAAO,oBAAoB,CAAC;IAC9C,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;IACnD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,gBAAgB,CAAC;CAC9G,CAAC;AAkfF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,eAAe,EACrB,aAAa,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,EACrD,qBAAqB,EAAE,MAAM,iBAAiB,EAC9C,mCAAmC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC5D,iBAAiB,CAWnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,EACrB,gBAAgB,EAAE,SAAS,EAC3B,mBAAmB,EAAE,kBAAkB,EAAE,EACzC,mCAAmC,GAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAe,GACvE,iBAAiB,CA4EnB"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAEV,YAAY,IAAI,gBAAgB,EAMhC,WAAW,IAAI,eAAe,EAC9B,cAAc,IAAI,kBAAkB,EACpC,iBAAiB,IAAI,qBAAqB,EAE1C,iBAAiB,EACjB,aAAa,IAAI,iBAAiB,EAClC,gBAAgB,IAAI,oBAAoB,EACzC,MAAM,SAAS,CAAC;AAGjB,OAAO,EAOL,KAAK,kBAAkB,EAGxB,MAAM,aAAa,CAAC;AAGrB,KAAK,eAAe,GAAG;IACrB,YAAY,EAAE,OAAO,gBAAgB,CAAC;IACtC,cAAc,EAAE,iBAAiB,CAAC;IAClC,YAAY,EAAE,iBAAiB,CAAC;IAChC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,WAAW,EAAE,OAAO,eAAe,CAAC;IACpC,cAAc,EAAE,OAAO,kBAAkB,CAAC;IAC1C,iBAAiB,EAAE,OAAO,qBAAqB,CAAC;IAChD,aAAa,EAAE,OAAO,iBAAiB,CAAC;IACxC,aAAa,EAAE,iBAAiB,CAAC;IACjC,gBAAgB,EAAE,OAAO,oBAAoB,CAAC;IAC9C,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;IACnD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,gBAAgB,CAAC;CAC9G,CAAC;AA4fF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,eAAe,EACrB,aAAa,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,EACrD,qBAAqB,EAAE,MAAM,iBAAiB,EAC9C,mCAAmC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC5D,iBAAiB,CAWnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,EACrB,gBAAgB,EAAE,SAAS,EAC3B,mBAAmB,EAAE,kBAAkB,EAAE,EACzC,mCAAmC,GAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAe,GACvE,iBAAiB,CA6EnB"}
@@ -234,7 +234,7 @@ function createResolverInvoker(deps, runtimeContainer, markAllowedCrossRealmGrap
234
234
  if (descriptor.scope === 'singleton') {
235
235
  const instance = await runtimeContainer.resolve(descriptor.token);
236
236
  const resolverMethod = resolveResolverMethod(instance, descriptor, handler);
237
- const methodArguments = handler.type === 'field' ? objectFieldResolvers.createMethodArguments(handler, source, contextValue) : [await createResolverInput(deps, handler, args, markAllowedCrossRealmGraphqlObjects), contextValue];
237
+ const methodArguments = handler.type === 'field' ? objectFieldResolvers.createMethodArguments(handler, await createResolverInput(deps, handler, args, markAllowedCrossRealmGraphqlObjects), source, contextValue) : [await createResolverInput(deps, handler, args, markAllowedCrossRealmGraphqlObjects), contextValue];
238
238
  return resolverMethod.call(instance, ...methodArguments);
239
239
  }
240
240
  const operationContainer = contextValue[GRAPHQL_OPERATION_CONTAINER] ?? runtimeContainer.createRequestScope();
@@ -242,7 +242,7 @@ function createResolverInvoker(deps, runtimeContainer, markAllowedCrossRealmGrap
242
242
  try {
243
243
  const instance = await operationContainer.resolve(descriptor.token);
244
244
  const resolverMethod = resolveResolverMethod(instance, descriptor, handler);
245
- const methodArguments = handler.type === 'field' ? objectFieldResolvers.createMethodArguments(handler, source, contextValue) : [await createResolverInput(deps, handler, args, markAllowedCrossRealmGraphqlObjects), contextValue];
245
+ const methodArguments = handler.type === 'field' ? objectFieldResolvers.createMethodArguments(handler, await createResolverInput(deps, handler, args, markAllowedCrossRealmGraphqlObjects), source, contextValue) : [await createResolverInput(deps, handler, args, markAllowedCrossRealmGraphqlObjects), contextValue];
246
246
  return await resolverMethod.call(instance, ...methodArguments);
247
247
  } finally {
248
248
  if (disposeOperationContainer) {
@@ -315,7 +315,7 @@ export function createCodeFirstSchema(deps, runtimeContainer, resolverDescriptor
315
315
  const invokeResolver = createResolverInvoker(deps, runtimeContainer, markAllowedCrossRealmGraphqlObjects, objectFieldResolvers);
316
316
  const outputTypeCache = new Map();
317
317
  function attachObjectFieldResolvers(typeName, fields) {
318
- return objectFieldResolvers.attach(typeName, fields, outputType => resolveRootOutputType(deps, outputTypeCache, markAllowedCrossRealmGraphqlObjects, outputType, attachObjectFieldResolvers), (descriptor, handler, source, contextValue) => invokeResolver(descriptor, handler, {}, contextValue, source));
318
+ return objectFieldResolvers.attach(typeName, fields, outputType => resolveRootOutputType(deps, outputTypeCache, markAllowedCrossRealmGraphqlObjects, outputType, attachObjectFieldResolvers), handler => createFieldArgs(deps, handler), (descriptor, handler, args, source, contextValue) => invokeResolver(descriptor, handler, args, contextValue, source));
319
319
  }
320
320
  const queryFields = pickFieldsByType(deps, resolverDescriptors, 'query', markAllowedCrossRealmGraphqlObjects, outputTypeCache, attachObjectFieldResolvers, invokeResolver);
321
321
  const mutationFields = pickFieldsByType(deps, resolverDescriptors, 'mutation', markAllowedCrossRealmGraphqlObjects, outputTypeCache, attachObjectFieldResolvers, invokeResolver);