@fluojs/graphql 1.0.3 → 1.1.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.
- package/README.ko.md +95 -7
- package/README.md +95 -7
- package/dist/decorators.d.ts +39 -0
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +102 -1
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/metadata.d.ts +21 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +68 -7
- package/dist/node/graphql-websocket-transport.d.ts +36 -0
- package/dist/node/graphql-websocket-transport.d.ts.map +1 -0
- package/dist/node/graphql-websocket-transport.js +156 -0
- package/dist/schema/object-field-resolvers.d.ts +17 -0
- package/dist/schema/object-field-resolvers.d.ts.map +1 -0
- package/dist/schema/object-field-resolvers.js +78 -0
- package/dist/schema/schema.d.ts +5 -1
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +75 -39
- package/dist/service.d.ts +3 -7
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +56 -156
- package/dist/transport/transport.d.ts.map +1 -1
- package/dist/transport/transport.js +8 -3
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -1
- package/package.json +7 -7
package/dist/metadata.js
CHANGED
|
@@ -3,9 +3,11 @@ void ensureSymbolMetadataPolyfill();
|
|
|
3
3
|
const standardResolverMetadataKey = Symbol.for('fluo.graphql.standard.resolver');
|
|
4
4
|
const standardHandlerMetadataKey = Symbol.for('fluo.graphql.standard.handler');
|
|
5
5
|
const standardArgFieldMetadataKey = Symbol.for('fluo.graphql.standard.arg-field');
|
|
6
|
+
const standardFieldResolverParameterMetadataKey = Symbol.for('fluo.graphql.standard.field-resolver-parameter');
|
|
6
7
|
const resolverMetadataStore = new WeakMap();
|
|
7
8
|
const handlerMetadataStore = new WeakMap();
|
|
8
9
|
const argFieldMetadataStore = new WeakMap();
|
|
10
|
+
const fieldResolverParameterMetadataStore = new WeakMap();
|
|
9
11
|
function cloneResolverMetadata(metadata) {
|
|
10
12
|
return {
|
|
11
13
|
typeName: metadata.typeName
|
|
@@ -16,10 +18,17 @@ function cloneHandlerMetadata(metadata) {
|
|
|
16
18
|
argTypes: metadata.argTypes,
|
|
17
19
|
fieldName: metadata.fieldName,
|
|
18
20
|
inputClass: metadata.inputClass,
|
|
21
|
+
nullable: metadata.nullable,
|
|
19
22
|
outputType: metadata.outputType,
|
|
20
23
|
type: metadata.type
|
|
21
24
|
};
|
|
22
25
|
}
|
|
26
|
+
function cloneFieldResolverParameterMetadata(metadata) {
|
|
27
|
+
return {
|
|
28
|
+
index: metadata.index,
|
|
29
|
+
kind: metadata.kind
|
|
30
|
+
};
|
|
31
|
+
}
|
|
23
32
|
function cloneArgFieldMetadata(metadata) {
|
|
24
33
|
return {
|
|
25
34
|
argName: metadata.argName,
|
|
@@ -39,6 +48,9 @@ function getStandardHandlerMap(target) {
|
|
|
39
48
|
function getStandardArgFieldMap(target) {
|
|
40
49
|
return getStandardConstructorMetadataBag(target)?.[standardArgFieldMetadataKey];
|
|
41
50
|
}
|
|
51
|
+
function getStandardFieldResolverParameterMap(target) {
|
|
52
|
+
return getStandardConstructorMetadataBag(target)?.[standardFieldResolverParameterMetadataKey];
|
|
53
|
+
}
|
|
42
54
|
function getOrCreateHandlerMetadataMap(target) {
|
|
43
55
|
let map = handlerMetadataStore.get(target);
|
|
44
56
|
if (!map) {
|
|
@@ -55,6 +67,14 @@ function getOrCreateArgFieldMetadataMap(target) {
|
|
|
55
67
|
}
|
|
56
68
|
return map;
|
|
57
69
|
}
|
|
70
|
+
function getOrCreateFieldResolverParameterMetadataMap(target) {
|
|
71
|
+
let map = fieldResolverParameterMetadataStore.get(target);
|
|
72
|
+
if (!map) {
|
|
73
|
+
map = new Map();
|
|
74
|
+
fieldResolverParameterMetadataStore.set(target, map);
|
|
75
|
+
}
|
|
76
|
+
return map;
|
|
77
|
+
}
|
|
58
78
|
function getMergedMetadataEntries(target, stored, standard, resolve) {
|
|
59
79
|
const storedMap = stored ?? new Map();
|
|
60
80
|
const standardMap = standard ?? new Map();
|
|
@@ -84,10 +104,11 @@ export function defineResolverMetadata(target, metadata) {
|
|
|
84
104
|
export function getResolverMetadata(target) {
|
|
85
105
|
const stored = resolverMetadataStore.get(target);
|
|
86
106
|
const standard = getStandardResolverMetadata(target);
|
|
87
|
-
|
|
107
|
+
const metadata = stored ?? standard;
|
|
108
|
+
if (!metadata) {
|
|
88
109
|
return undefined;
|
|
89
110
|
}
|
|
90
|
-
return cloneResolverMetadata(
|
|
111
|
+
return cloneResolverMetadata(metadata);
|
|
91
112
|
}
|
|
92
113
|
|
|
93
114
|
/**
|
|
@@ -111,10 +132,11 @@ export function defineResolverHandlerMetadata(target, propertyKey, metadata) {
|
|
|
111
132
|
export function getResolverHandlerMetadata(target, propertyKey) {
|
|
112
133
|
const stored = handlerMetadataStore.get(target)?.get(propertyKey);
|
|
113
134
|
const standard = getStandardHandlerMap(target)?.get(propertyKey);
|
|
114
|
-
|
|
135
|
+
const metadata = stored ?? standard;
|
|
136
|
+
if (!metadata) {
|
|
115
137
|
return undefined;
|
|
116
138
|
}
|
|
117
|
-
return cloneHandlerMetadata(
|
|
139
|
+
return cloneHandlerMetadata(metadata);
|
|
118
140
|
}
|
|
119
141
|
|
|
120
142
|
/**
|
|
@@ -148,10 +170,11 @@ export function defineArgFieldMetadata(target, propertyKey, metadata) {
|
|
|
148
170
|
export function getArgFieldMetadata(target, propertyKey) {
|
|
149
171
|
const stored = argFieldMetadataStore.get(target)?.get(propertyKey);
|
|
150
172
|
const standard = getStandardArgFieldMap(target)?.get(propertyKey);
|
|
151
|
-
|
|
173
|
+
const metadata = stored ?? standard;
|
|
174
|
+
if (!metadata) {
|
|
152
175
|
return undefined;
|
|
153
176
|
}
|
|
154
|
-
return cloneArgFieldMetadata(
|
|
177
|
+
return cloneArgFieldMetadata(metadata);
|
|
155
178
|
}
|
|
156
179
|
|
|
157
180
|
/**
|
|
@@ -164,6 +187,40 @@ export function getArgFieldMetadataEntries(target) {
|
|
|
164
187
|
return getMergedMetadataEntries(target, argFieldMetadataStore.get(target), getStandardArgFieldMap(target), getArgFieldMetadata);
|
|
165
188
|
}
|
|
166
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Define one positional parameter binding for an object field resolver.
|
|
192
|
+
*
|
|
193
|
+
* @param target Resolver prototype that owns the decorated method.
|
|
194
|
+
* @param propertyKey Decorated method key.
|
|
195
|
+
* @param metadata Positional binding metadata to store.
|
|
196
|
+
*/
|
|
197
|
+
export function defineFieldResolverParameterMetadata(target, propertyKey, metadata) {
|
|
198
|
+
const methods = getOrCreateFieldResolverParameterMetadataMap(target);
|
|
199
|
+
const bindings = methods.get(propertyKey) ?? new Map();
|
|
200
|
+
bindings.set(metadata.index, cloneFieldResolverParameterMetadata(metadata));
|
|
201
|
+
methods.set(propertyKey, bindings);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get the positional parameter bindings declared for one resolver method.
|
|
206
|
+
*
|
|
207
|
+
* @param target Resolver prototype that owns the decorated method.
|
|
208
|
+
* @param propertyKey Decorated method key.
|
|
209
|
+
* @returns Parameter bindings ordered by their method parameter index.
|
|
210
|
+
*/
|
|
211
|
+
export function getFieldResolverParameterMetadataEntries(target, propertyKey) {
|
|
212
|
+
const stored = fieldResolverParameterMetadataStore.get(target)?.get(propertyKey);
|
|
213
|
+
const standard = getStandardFieldResolverParameterMap(target)?.get(propertyKey);
|
|
214
|
+
const merged = new Map();
|
|
215
|
+
for (const [index, metadata] of standard ?? []) {
|
|
216
|
+
merged.set(index, cloneFieldResolverParameterMetadata(metadata));
|
|
217
|
+
}
|
|
218
|
+
for (const [index, metadata] of stored ?? []) {
|
|
219
|
+
merged.set(index, cloneFieldResolverParameterMetadata(metadata));
|
|
220
|
+
}
|
|
221
|
+
return Array.from(merged.values()).sort((left, right) => left.index - right.index);
|
|
222
|
+
}
|
|
223
|
+
|
|
167
224
|
/**
|
|
168
225
|
* Provides the resolver metadata symbol value.
|
|
169
226
|
*/
|
|
@@ -175,4 +232,8 @@ export const handlerMetadataSymbol = standardHandlerMetadataKey;
|
|
|
175
232
|
/**
|
|
176
233
|
* Provides the arg metadata symbol value.
|
|
177
234
|
*/
|
|
178
|
-
export const argMetadataSymbol = standardArgFieldMetadataKey;
|
|
235
|
+
export const argMetadataSymbol = standardArgFieldMetadataKey;
|
|
236
|
+
/**
|
|
237
|
+
* Provides the object field-resolver parameter metadata symbol value.
|
|
238
|
+
*/
|
|
239
|
+
export const fieldResolverParameterMetadataSymbol = standardFieldResolverParameterMetadataKey;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FrameworkRequest, HttpApplicationAdapter } from '@fluojs/http';
|
|
2
|
+
import type { ExecutionArgs, execute as executeGraphql, GraphQLError as GraphQLErrorType, subscribe as subscribeGraphql } from 'graphql';
|
|
3
|
+
interface GraphqlNodeWebSocketLimits {
|
|
4
|
+
maxConnections: number;
|
|
5
|
+
maxOperationsPerConnection: number;
|
|
6
|
+
maxPayloadBytes: number;
|
|
7
|
+
}
|
|
8
|
+
interface GraphqlSubscribePayload {
|
|
9
|
+
operationName?: string | null;
|
|
10
|
+
query: string;
|
|
11
|
+
variables?: Record<string, unknown> | null;
|
|
12
|
+
}
|
|
13
|
+
export interface GraphqlNodeWebSocketSubscribeRequest {
|
|
14
|
+
connectionParams?: Record<string, unknown>;
|
|
15
|
+
operationId: string;
|
|
16
|
+
payload: GraphqlSubscribePayload;
|
|
17
|
+
request: FrameworkRequest;
|
|
18
|
+
socket: object;
|
|
19
|
+
}
|
|
20
|
+
export interface GraphqlNodeWebSocketTransport {
|
|
21
|
+
dispose(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
interface GraphqlNodeWebSocketTransportOptions {
|
|
24
|
+
adapter: HttpApplicationAdapter;
|
|
25
|
+
connectionInitWaitTimeoutMs?: number;
|
|
26
|
+
execute: typeof executeGraphql;
|
|
27
|
+
keepAliveMs?: number;
|
|
28
|
+
limits?: GraphqlNodeWebSocketLimits;
|
|
29
|
+
onComplete: (socketKey: object, operationId: string) => Promise<void> | void;
|
|
30
|
+
onDisconnect: (socketKey: object) => Promise<void> | void;
|
|
31
|
+
onSubscribe: (request: GraphqlNodeWebSocketSubscribeRequest) => Promise<ExecutionArgs | readonly GraphQLErrorType[]>;
|
|
32
|
+
subscribe: typeof subscribeGraphql;
|
|
33
|
+
}
|
|
34
|
+
export declare function createNodeGraphqlWebSocketTransport(options: GraphqlNodeWebSocketTransportOptions): Promise<GraphqlNodeWebSocketTransport>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=graphql-websocket-transport.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { handleProtocols } from 'graphql-ws';
|
|
2
|
+
import { useServer } from 'graphql-ws/lib/use/ws';
|
|
3
|
+
import { WebSocketServer } from 'ws';
|
|
4
|
+
import { isGraphqlPath } from '../transport/transport.js';
|
|
5
|
+
function hasNodeUpgradeServer(value) {
|
|
6
|
+
if (typeof value !== 'object' || value === null) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const server = value;
|
|
10
|
+
return typeof server.on === 'function' && typeof server.off === 'function';
|
|
11
|
+
}
|
|
12
|
+
function isConnectionParamsRecord(value) {
|
|
13
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
function buildFrameworkRequestFromIncomingMessage(request) {
|
|
16
|
+
const requestUrl = new URL(request.url ?? '/graphql', 'http://localhost');
|
|
17
|
+
return {
|
|
18
|
+
cookies: {},
|
|
19
|
+
headers: request.headers,
|
|
20
|
+
method: request.method ?? 'GET',
|
|
21
|
+
params: {},
|
|
22
|
+
path: requestUrl.pathname,
|
|
23
|
+
query: Object.fromEntries(requestUrl.searchParams.entries()),
|
|
24
|
+
raw: request,
|
|
25
|
+
url: requestUrl.pathname + requestUrl.search
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function closeWebSocketServer(server) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
server.close(error => {
|
|
31
|
+
if (error?.message === 'The server is not running') {
|
|
32
|
+
resolve();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (error) {
|
|
36
|
+
reject(error);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
resolve();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function resolveUpgradeServer(adapter) {
|
|
44
|
+
if (typeof adapter.getServer !== 'function') {
|
|
45
|
+
throw new Error('GraphQL websocket subscriptions require an HTTP adapter with getServer(). Use the Node HTTP adapter or provide a compatible adapter implementation.');
|
|
46
|
+
}
|
|
47
|
+
const server = adapter.getServer();
|
|
48
|
+
if (!hasNodeUpgradeServer(server)) {
|
|
49
|
+
throw new Error('GraphQL websocket subscriptions require adapter.getServer() to return a Node HTTP/S server that supports upgrade listeners.');
|
|
50
|
+
}
|
|
51
|
+
return server;
|
|
52
|
+
}
|
|
53
|
+
function rejectWebSocketUpgrade(socket, statusCode, message) {
|
|
54
|
+
if (!socket.writable) {
|
|
55
|
+
socket.destroy();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const body = `${message}\n`;
|
|
59
|
+
socket.write([`HTTP/1.1 ${String(statusCode)} ${statusCode === 503 ? 'Service Unavailable' : 'Bad Request'}`, 'Connection: close', 'Content-Type: text/plain; charset=utf-8', `Content-Length: ${String(new TextEncoder().encode(body).byteLength)}`, '', body].join('\r\n'));
|
|
60
|
+
socket.destroy();
|
|
61
|
+
}
|
|
62
|
+
function createUpgradeListener(websocketServer, limits) {
|
|
63
|
+
return (request, socket, head) => {
|
|
64
|
+
const targetPath = new URL(request.url ?? '/', 'http://localhost').pathname;
|
|
65
|
+
if (!isGraphqlPath(targetPath)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (limits && websocketServer.clients.size >= limits.maxConnections) {
|
|
69
|
+
rejectWebSocketUpgrade(socket, 503, 'GraphQL websocket connection count exceeds the configured limit.');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
websocketServer.handleUpgrade(request, socket, head, websocket => {
|
|
73
|
+
websocketServer.emit('connection', websocket, request);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function createSubscribeRequest(context, message) {
|
|
78
|
+
return {
|
|
79
|
+
connectionParams: isConnectionParamsRecord(context.connectionParams) ? context.connectionParams : undefined,
|
|
80
|
+
operationId: message.id,
|
|
81
|
+
payload: message.payload,
|
|
82
|
+
request: buildFrameworkRequestFromIncomingMessage(context.extra.request),
|
|
83
|
+
socket: context.extra.socket
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export async function createNodeGraphqlWebSocketTransport(options) {
|
|
87
|
+
const upgradeServer = resolveUpgradeServer(options.adapter);
|
|
88
|
+
const disconnectErrors = [];
|
|
89
|
+
const pendingDisconnects = new Set();
|
|
90
|
+
const websocketServer = new WebSocketServer({
|
|
91
|
+
handleProtocols: protocols => handleProtocols(protocols),
|
|
92
|
+
maxPayload: options.limits?.maxPayloadBytes ?? 0,
|
|
93
|
+
noServer: true
|
|
94
|
+
});
|
|
95
|
+
const upgradeListener = createUpgradeListener(websocketServer, options.limits);
|
|
96
|
+
const trackDisconnect = socketKey => {
|
|
97
|
+
const pendingDisconnect = Promise.resolve(options.onDisconnect(socketKey)).catch(error => {
|
|
98
|
+
disconnectErrors.push(error);
|
|
99
|
+
}).finally(() => {
|
|
100
|
+
pendingDisconnects.delete(pendingDisconnect);
|
|
101
|
+
});
|
|
102
|
+
pendingDisconnects.add(pendingDisconnect);
|
|
103
|
+
};
|
|
104
|
+
const trackConnection = websocket => {
|
|
105
|
+
websocket.once('close', () => trackDisconnect(websocket));
|
|
106
|
+
};
|
|
107
|
+
const websocketDisposable = useServer({
|
|
108
|
+
connectionInitWaitTimeout: options.connectionInitWaitTimeoutMs,
|
|
109
|
+
execute: options.execute,
|
|
110
|
+
onComplete: async (context, message) => {
|
|
111
|
+
await options.onComplete(context.extra.socket, message.id);
|
|
112
|
+
},
|
|
113
|
+
onDisconnect: async context => {
|
|
114
|
+
await options.onDisconnect(context.extra.socket);
|
|
115
|
+
},
|
|
116
|
+
onSubscribe: async (context, message) => options.onSubscribe(createSubscribeRequest(context, message)),
|
|
117
|
+
subscribe: options.subscribe
|
|
118
|
+
}, websocketServer, options.keepAliveMs);
|
|
119
|
+
websocketServer.on('connection', trackConnection);
|
|
120
|
+
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
|
+
}
|
|
133
|
+
for (const result of disconnectResults) {
|
|
134
|
+
if (result.status === 'rejected') {
|
|
135
|
+
disposeError ??= result.reason;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
await websocketDisposable.dispose();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
disposeError = error;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
await closeWebSocketServer(websocketServer);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
disposeError ??= error;
|
|
147
|
+
}
|
|
148
|
+
if (disposeError instanceof Error) {
|
|
149
|
+
throw disposeError;
|
|
150
|
+
}
|
|
151
|
+
if (disposeError !== undefined) {
|
|
152
|
+
throw new Error(String(disposeError));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GraphQLFieldConfigMap, GraphQLOutputType } from 'graphql';
|
|
2
|
+
import type { GraphQLContext as FluoGraphQLContext, GraphqlRootOutputType, ResolverDescriptor, ResolverHandlerDescriptor } from '../types.js';
|
|
3
|
+
type ResolveOutputType = (outputType: GraphqlRootOutputType) => GraphQLOutputType;
|
|
4
|
+
type InvokeObjectFieldResolver = (descriptor: ResolverDescriptor, handler: ResolverHandlerDescriptor, source: unknown, contextValue: FluoGraphQLContext) => Promise<unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* Indexes object field resolver descriptors and attaches them to matching code-first object types.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ObjectFieldResolverRegistry {
|
|
9
|
+
private readonly attachedTypeNames;
|
|
10
|
+
private readonly entriesByTypeName;
|
|
11
|
+
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[];
|
|
14
|
+
assertAllTargetsAttached(): void;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=object-field-resolvers.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function assertNever(value) {
|
|
2
|
+
throw new Error(`Unsupported GraphQL field resolver parameter binding: ${String(value)}`);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Indexes object field resolver descriptors and attaches them to matching code-first object types.
|
|
7
|
+
*/
|
|
8
|
+
export class ObjectFieldResolverRegistry {
|
|
9
|
+
attachedTypeNames = new Set();
|
|
10
|
+
entriesByTypeName = new Map();
|
|
11
|
+
constructor(descriptors) {
|
|
12
|
+
for (const descriptor of descriptors) {
|
|
13
|
+
for (const handler of descriptor.handlers) {
|
|
14
|
+
if (handler.type !== 'field') {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const fields = this.entriesByTypeName.get(descriptor.typeName) ?? new Map();
|
|
18
|
+
if (fields.has(handler.fieldName)) {
|
|
19
|
+
throw new Error(`GraphQL schema conflict: object field resolver "${descriptor.typeName}.${handler.fieldName}" is registered more than once.`);
|
|
20
|
+
}
|
|
21
|
+
fields.set(handler.fieldName, {
|
|
22
|
+
descriptor,
|
|
23
|
+
handler
|
|
24
|
+
});
|
|
25
|
+
this.entriesByTypeName.set(descriptor.typeName, fields);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
attach(typeName, fields, resolveOutputType, invokeResolver) {
|
|
30
|
+
const resolverFields = this.entriesByTypeName.get(typeName);
|
|
31
|
+
if (!resolverFields) {
|
|
32
|
+
return fields;
|
|
33
|
+
}
|
|
34
|
+
this.attachedTypeNames.add(typeName);
|
|
35
|
+
const attachedFields = {
|
|
36
|
+
...fields
|
|
37
|
+
};
|
|
38
|
+
for (const [fieldName, entry] of resolverFields) {
|
|
39
|
+
const existingField = attachedFields[fieldName];
|
|
40
|
+
const outputType = entry.handler.outputType ? resolveOutputType(entry.handler.outputType) : existingField?.type;
|
|
41
|
+
if (!outputType) {
|
|
42
|
+
throw new Error(`GraphQL object field resolver "${typeName}.${fieldName}" must target an existing field or declare a type.`);
|
|
43
|
+
}
|
|
44
|
+
attachedFields[fieldName] = {
|
|
45
|
+
...existingField,
|
|
46
|
+
resolve: async (source, _args, contextValue) => invokeResolver(entry.descriptor, entry.handler, source, contextValue),
|
|
47
|
+
type: outputType
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return attachedFields;
|
|
51
|
+
}
|
|
52
|
+
createMethodArguments(handler, parent, contextValue) {
|
|
53
|
+
const lastBinding = handler.parameterBindings.at(-1);
|
|
54
|
+
const methodArguments = Array.from({
|
|
55
|
+
length: (lastBinding?.index ?? -1) + 1
|
|
56
|
+
});
|
|
57
|
+
for (const binding of handler.parameterBindings) {
|
|
58
|
+
switch (binding.kind) {
|
|
59
|
+
case 'parent':
|
|
60
|
+
methodArguments[binding.index] = parent;
|
|
61
|
+
break;
|
|
62
|
+
case 'context':
|
|
63
|
+
methodArguments[binding.index] = contextValue;
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
assertNever(binding.kind);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return methodArguments;
|
|
70
|
+
}
|
|
71
|
+
assertAllTargetsAttached() {
|
|
72
|
+
for (const typeName of this.entriesByTypeName.keys()) {
|
|
73
|
+
if (!this.attachedTypeNames.has(typeName)) {
|
|
74
|
+
throw new Error(`GraphQL object field resolver target type "${typeName}" is not reachable from a code-first root operation output type.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/schema/schema.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Container } from '@fluojs/di';
|
|
2
|
-
import type { GraphQLError as GraphQLErrorType, GraphQLList as GraphQLListType, GraphQLObjectType as GraphQLObjectTypeType, GraphQLSchema as GraphQLSchemaType,
|
|
2
|
+
import type { GraphQLError as GraphQLErrorType, GraphQLList as GraphQLListType, GraphQLNonNull as GraphQLNonNullType, GraphQLObjectType as GraphQLObjectTypeType, GraphQLScalarType, GraphQLSchema as GraphQLSchemaType, GraphQLUnionType as GraphQLUnionTypeType } from 'graphql';
|
|
3
3
|
import { type ResolverDescriptor } from '../types.js';
|
|
4
4
|
type YogaGraphqlDeps = {
|
|
5
5
|
GraphQLError: typeof GraphQLErrorType;
|
|
@@ -8,11 +8,15 @@ type YogaGraphqlDeps = {
|
|
|
8
8
|
GraphQLID: GraphQLScalarType;
|
|
9
9
|
GraphQLInt: GraphQLScalarType;
|
|
10
10
|
GraphQLList: typeof GraphQLListType;
|
|
11
|
+
GraphQLNonNull: typeof GraphQLNonNullType;
|
|
11
12
|
GraphQLObjectType: typeof GraphQLObjectTypeType;
|
|
12
13
|
GraphQLSchema: typeof GraphQLSchemaType;
|
|
13
14
|
GraphQLString: GraphQLScalarType;
|
|
14
15
|
GraphQLUnionType: typeof GraphQLUnionTypeType;
|
|
15
16
|
buildSchema: (source: string) => GraphQLSchemaType;
|
|
17
|
+
createGraphQLError: (message: string, options: {
|
|
18
|
+
extensions?: Record<string, unknown>;
|
|
19
|
+
}) => GraphQLErrorType;
|
|
16
20
|
};
|
|
17
21
|
/**
|
|
18
22
|
* Resolve schema.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema/schema.ts"],"names":[],"mappings":"
|
|
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"}
|