@nifrajs/graphql 3.5.0 → 4.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.
- package/dist/limits.d.ts +5 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +42 -29
- package/dist/limits.js.map +1 -1
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +34 -7
- package/dist/ws.js.map +1 -1
- package/package.json +5 -2
- package/src/limits.ts +44 -41
- package/src/ws.ts +43 -11
package/dist/limits.d.ts
CHANGED
|
@@ -32,6 +32,11 @@ export interface GraphqlDocumentMetrics {
|
|
|
32
32
|
readonly aliases: number;
|
|
33
33
|
readonly complexity: number;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Each fragment is measured once and reused at every spread. Expanding spreads in place costs a visit
|
|
37
|
+
* per expanded field, and a document whose fragments each spread the one before twice turns n short
|
|
38
|
+
* lines into 2^n visits, all of it synchronous before any limit can refuse the document.
|
|
39
|
+
*/
|
|
35
40
|
export declare function documentMetrics(document: DocumentNode): GraphqlDocumentMetrics;
|
|
36
41
|
export declare function requestLimitError(query: string, variables: Record<string, unknown> | null, limits: GraphqlLimits): string | undefined;
|
|
37
42
|
export declare function documentLimitError(metrics: GraphqlDocumentMetrics, limits: GraphqlLimits): string | undefined;
|
package/dist/limits.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAA4B,MAAM,SAAS,CAAA;AAExF,eAAO,MAAM,sBAAsB;;;;;;;EAOjC,CAAA;AAEF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAUD,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CA6B/E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,MAAM,CAE5E;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAA4B,MAAM,SAAS,CAAA;AAExF,eAAO,MAAM,sBAAsB;;;;;;;EAOjC,CAAA;AAEF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAUD,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CA6B/E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,MAAM,CAE5E;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAqCD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,sBAAsB,CAmC9E;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACzC,MAAM,EAAE,aAAa,GACpB,MAAM,GAAG,SAAS,CAQpB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE,aAAa,GACpB,MAAM,GAAG,SAAS,CAcpB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAM/E;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,SAAS,GAAG,WAAW,EAC9B,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,GAAG,WAAW,KAAK,OAAO,GAC3E,OAAO,CAST"}
|
package/dist/limits.js
CHANGED
|
@@ -30,52 +30,65 @@ export function utf8Bytes(value) {
|
|
|
30
30
|
export function variablesBytes(value) {
|
|
31
31
|
return value === null ? 0 : utf8Bytes(JSON.stringify(value));
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const NO_SELECTIONS = { depth: 0, aliases: 0, complexity: 0 };
|
|
34
|
+
/** `depth` counts from the selection set's own level, so one fragment measure holds at every spread. */
|
|
35
|
+
function selectionsMetrics(selections, fragment) {
|
|
36
|
+
let depth = 0;
|
|
35
37
|
let aliases = 0;
|
|
36
38
|
let complexity = 0;
|
|
37
39
|
for (const selection of selections) {
|
|
40
|
+
let nested;
|
|
38
41
|
if (selection.kind === Kind.FIELD) {
|
|
39
42
|
complexity += 1;
|
|
40
43
|
if (selection.alias !== undefined)
|
|
41
44
|
aliases += 1;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
maxDepth = Math.max(maxDepth, depth + 1);
|
|
50
|
-
}
|
|
51
|
-
continue;
|
|
45
|
+
nested =
|
|
46
|
+
selection.selectionSet === undefined
|
|
47
|
+
? NO_SELECTIONS
|
|
48
|
+
: selectionsMetrics(selection.selectionSet.selections, fragment);
|
|
49
|
+
depth = Math.max(depth, nested.depth + 1);
|
|
52
50
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
else {
|
|
52
|
+
nested =
|
|
53
|
+
selection.kind === Kind.INLINE_FRAGMENT
|
|
54
|
+
? selectionsMetrics(selection.selectionSet.selections, fragment)
|
|
55
|
+
: fragment(selection.name.value);
|
|
56
|
+
depth = Math.max(depth, nested.depth);
|
|
59
57
|
}
|
|
60
|
-
const nestedSelections = fragments.get(selection.name.value);
|
|
61
|
-
if (nestedSelections === undefined || activeFragments.has(selection.name.value))
|
|
62
|
-
continue;
|
|
63
|
-
const nextActive = new Set(activeFragments);
|
|
64
|
-
nextActive.add(selection.name.value);
|
|
65
|
-
const nested = selectionsMetrics(nestedSelections, fragments, depth, nextActive);
|
|
66
|
-
maxDepth = Math.max(maxDepth, nested.depth);
|
|
67
58
|
aliases += nested.aliases;
|
|
68
59
|
complexity += nested.complexity;
|
|
69
60
|
}
|
|
70
|
-
return { depth
|
|
61
|
+
return { depth, aliases, complexity };
|
|
71
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Each fragment is measured once and reused at every spread. Expanding spreads in place costs a visit
|
|
65
|
+
* per expanded field, and a document whose fragments each spread the one before twice turns n short
|
|
66
|
+
* lines into 2^n visits, all of it synchronous before any limit can refuse the document.
|
|
67
|
+
*/
|
|
72
68
|
export function documentMetrics(document) {
|
|
73
|
-
const
|
|
69
|
+
const definitions = new Map();
|
|
74
70
|
for (const definition of document.definitions) {
|
|
75
71
|
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
|
|
76
|
-
|
|
72
|
+
definitions.set(definition.name.value, definition.selectionSet.selections);
|
|
77
73
|
}
|
|
78
74
|
}
|
|
75
|
+
const measured = new Map();
|
|
76
|
+
const measuring = new Set();
|
|
77
|
+
const fragment = (name) => {
|
|
78
|
+
const known = measured.get(name);
|
|
79
|
+
if (known !== undefined)
|
|
80
|
+
return known;
|
|
81
|
+
const selections = definitions.get(name);
|
|
82
|
+
// An unknown fragment and a cycle back into one being measured, both refused by validation,
|
|
83
|
+
// add nothing.
|
|
84
|
+
if (selections === undefined || measuring.has(name))
|
|
85
|
+
return NO_SELECTIONS;
|
|
86
|
+
measuring.add(name);
|
|
87
|
+
const metrics = selectionsMetrics(selections, fragment);
|
|
88
|
+
measuring.delete(name);
|
|
89
|
+
measured.set(name, metrics);
|
|
90
|
+
return metrics;
|
|
91
|
+
};
|
|
79
92
|
let operations = 0;
|
|
80
93
|
let depth = 0;
|
|
81
94
|
let aliases = 0;
|
|
@@ -84,7 +97,7 @@ export function documentMetrics(document) {
|
|
|
84
97
|
if (definition.kind !== Kind.OPERATION_DEFINITION)
|
|
85
98
|
continue;
|
|
86
99
|
operations += 1;
|
|
87
|
-
const metrics = selectionsMetrics(definition.selectionSet.selections,
|
|
100
|
+
const metrics = selectionsMetrics(definition.selectionSet.selections, fragment);
|
|
88
101
|
depth = Math.max(depth, metrics.depth);
|
|
89
102
|
aliases += metrics.aliases;
|
|
90
103
|
complexity += metrics.complexity;
|
package/dist/limits.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,IAAI,EAAsB,MAAM,SAAS,CAAA;AAExF,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,CAAC;IAClD,aAAa,EAAE,EAAE,GAAG,IAAI;IACxB,iBAAiB,EAAE,GAAG,GAAG,IAAI;IAC7B,QAAQ,EAAE,EAAE;IACZ,UAAU,EAAE,GAAG;IACf,aAAa,EAAE,KAAK;IACpB,aAAa,EAAE,EAAE;CAClB,CAAC,CAAA;AAoBF,SAAS,KAAK,CAAC,KAAyB,EAAE,QAAgB,EAAE,IAAY,EAAE,OAAe;IACvF,MAAM,MAAM,GAAG,KAAK,IAAI,QAAQ,CAAA;IAChC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,OAAO,EAAE,CAAC;QACtD,MAAM,IAAI,UAAU,CAAC,WAAW,IAAI,8BAA8B,OAAO,EAAE,CAAC,CAAA;IAC9E,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAgC,EAAE;IAC9D,OAAO;QACL,aAAa,EAAE,KAAK,CAClB,OAAO,CAAC,aAAa,EACrB,sBAAsB,CAAC,aAAa,EACpC,eAAe,EACf,CAAC,CACF;QACD,iBAAiB,EAAE,KAAK,CACtB,OAAO,CAAC,iBAAiB,EACzB,sBAAsB,CAAC,iBAAiB,EACxC,mBAAmB,EACnB,CAAC,CACF;QACD,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;QACjF,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,sBAAsB,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;QACzF,aAAa,EAAE,KAAK,CAClB,OAAO,CAAC,aAAa,EACrB,sBAAsB,CAAC,aAAa,EACpC,eAAe,EACf,CAAC,CACF;QACD,aAAa,EAAE,KAAK,CAClB,OAAO,CAAC,aAAa,EACrB,sBAAsB,CAAC,aAAa,EACpC,eAAe,EACf,CAAC,CACF;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,UAAU,CAAA;AACnD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAqC;IAClE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;AAC9D,CAAC;
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"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"}
|
package/dist/ws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAoC,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAC1F,OAAO,
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAoC,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAC1F,OAAO,EAKL,KAAK,aAAa,EAKnB,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,KAAK,qBAAqB,EAI1B,KAAK,aAAa,EACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAgB,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAA;AASvE,2EAA2E;AAC3E,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,kFAAkF;IAClF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACpC;AAED,MAAM,WAAW,gBAAgB,CAC/B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjE,6BAA6B;IAC7B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,kGAAkG;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IACjD;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CACnB,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,EACzC,OAAO,EAAE,OAAO,KACb,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/B,sFAAsF;IACtF,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;IAC/C,0DAA0D;IAC1D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,gFAAgF;IAChF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CACrC;AA4HD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChG,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,GACjC,gBAAgB,CAAC,mBAAmB,CAAC,CAsHvC"}
|
package/dist/ws.js
CHANGED
|
@@ -80,11 +80,19 @@ function maskWsError(error) {
|
|
|
80
80
|
...(Array.isArray(record.path) ? { path: record.path } : {}),
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
/**
|
|
84
|
+
* A frame graphql-ws sends, execution errors masked. A `complete` or `error` frame ends its operation,
|
|
85
|
+
* so the id stops counting against `maxSubscriptions`: a client is not expected to complete an
|
|
86
|
+
* operation the server already ended.
|
|
87
|
+
*/
|
|
88
|
+
function outboundFrame(message, subscriptions) {
|
|
84
89
|
try {
|
|
85
90
|
const parsed = recordOf(JSON.parse(message));
|
|
86
91
|
if (parsed === undefined)
|
|
87
92
|
return message;
|
|
93
|
+
if ((parsed.type === "complete" || parsed.type === "error") && typeof parsed.id === "string") {
|
|
94
|
+
subscriptions.delete(parsed.id);
|
|
95
|
+
}
|
|
88
96
|
if (parsed.type === "next") {
|
|
89
97
|
const payload = recordOf(parsed.payload);
|
|
90
98
|
if (payload !== undefined && Array.isArray(payload.errors)) {
|
|
@@ -103,12 +111,16 @@ function maskWsFrame(message) {
|
|
|
103
111
|
}
|
|
104
112
|
return message;
|
|
105
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* A timeout is that operation's error result. graphql-ws does not catch a rejected execute or
|
|
116
|
+
* subscribe, so a rejection would end the whole socket instead of the one operation.
|
|
117
|
+
*/
|
|
106
118
|
async function withWsTimeout(work, timeoutMs) {
|
|
107
119
|
if (!(work instanceof Promise) || timeoutMs === 0)
|
|
108
120
|
return work;
|
|
109
121
|
let timer;
|
|
110
|
-
const timeout = new Promise((
|
|
111
|
-
timer = setTimeout(() =>
|
|
122
|
+
const timeout = new Promise((resolve) => {
|
|
123
|
+
timer = setTimeout(() => resolve({ errors: [new GraphQLError("Execution timed out.")] }), timeoutMs);
|
|
112
124
|
});
|
|
113
125
|
try {
|
|
114
126
|
return await Promise.race([work, timeout]);
|
|
@@ -150,7 +162,12 @@ export function graphqlWebSocket(options) {
|
|
|
150
162
|
subscribe: subscribeWithTimeout,
|
|
151
163
|
onConnect: options.onConnect
|
|
152
164
|
? async (ctx) => {
|
|
153
|
-
|
|
165
|
+
try {
|
|
166
|
+
return await options.onConnect?.(ctx.connectionParams, ctx.extra.request);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
154
171
|
}
|
|
155
172
|
: undefined,
|
|
156
173
|
context: options.context
|
|
@@ -167,7 +184,7 @@ export function graphqlWebSocket(options) {
|
|
|
167
184
|
const closed = gqlServer.opened({
|
|
168
185
|
protocol,
|
|
169
186
|
send: async (data) => {
|
|
170
|
-
ws.send(
|
|
187
|
+
ws.send(outboundFrame(data, ws.data.subscriptions));
|
|
171
188
|
},
|
|
172
189
|
close: (code, reason) => {
|
|
173
190
|
ws.close(code, reason);
|
|
@@ -177,7 +194,16 @@ export function graphqlWebSocket(options) {
|
|
|
177
194
|
},
|
|
178
195
|
}, { request: ws.data.request });
|
|
179
196
|
ws.data.closed = closed;
|
|
180
|
-
ws.data.dispatch = (message) =>
|
|
197
|
+
ws.data.dispatch = async (message) => {
|
|
198
|
+
try {
|
|
199
|
+
await onFrame?.(message);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// graphql-ws leaves a failed frame (a throwing context builder, say) to its socket adapter.
|
|
203
|
+
// Unhandled, the rejection would end a Bun or Node process; the protocol closes one socket.
|
|
204
|
+
ws.close(4500, "Internal server error");
|
|
205
|
+
}
|
|
206
|
+
};
|
|
181
207
|
},
|
|
182
208
|
message(ws, data) {
|
|
183
209
|
// Fire-and-forget: graphql-ws's per-message handler does not resolve until a subscription's
|
|
@@ -218,7 +244,8 @@ export function graphqlWebSocket(options) {
|
|
|
218
244
|
},
|
|
219
245
|
close(ws, code, reason) {
|
|
220
246
|
ws.data.subscriptions.clear();
|
|
221
|
-
|
|
247
|
+
// The socket is gone: a failing iterator return() or onClose hook has no one left to answer.
|
|
248
|
+
void Promise.resolve(ws.data.closed?.(code, reason)).catch(() => { });
|
|
222
249
|
},
|
|
223
250
|
};
|
|
224
251
|
}
|
package/dist/ws.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/graphql",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Mount a GraphQL endpoint on a nifra app - a spec-compliant GraphQL-over-HTTP handler you mount at POST /graphql, graphql-ws subscriptions over nifra's native WebSocket lane, and typed nifra context piped straight into your resolvers. Uses graphql's own execute/subscribe; no server framework re-bundled.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"api"
|
|
15
15
|
],
|
|
16
16
|
"type": "module",
|
|
17
|
+
"nifra": {
|
|
18
|
+
"environment": "library"
|
|
19
|
+
},
|
|
17
20
|
"main": "./dist/index.js",
|
|
18
21
|
"module": "./dist/index.js",
|
|
19
22
|
"types": "./dist/index.d.ts",
|
|
@@ -43,7 +46,7 @@
|
|
|
43
46
|
"build": "tsc -p tsconfig.build.json && bun run ../../scripts/fix-dts.ts dist"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
46
|
-
"@nifrajs/core": "^
|
|
49
|
+
"@nifrajs/core": "^4.0.0",
|
|
47
50
|
"graphql": "^17.0.0",
|
|
48
51
|
"graphql-ws": "^6.0.0"
|
|
49
52
|
},
|
package/src/limits.ts
CHANGED
|
@@ -81,65 +81,68 @@ export interface GraphqlDocumentMetrics {
|
|
|
81
81
|
readonly complexity: number
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
type SelectionMetrics = Omit<GraphqlDocumentMetrics, "operations">
|
|
85
|
+
|
|
86
|
+
const NO_SELECTIONS: SelectionMetrics = { depth: 0, aliases: 0, complexity: 0 }
|
|
87
|
+
|
|
88
|
+
/** `depth` counts from the selection set's own level, so one fragment measure holds at every spread. */
|
|
84
89
|
function selectionsMetrics(
|
|
85
90
|
selections: readonly SelectionNode[],
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
): Omit<GraphqlDocumentMetrics, "operations"> {
|
|
90
|
-
let maxDepth = depth
|
|
91
|
+
fragment: (name: string) => SelectionMetrics,
|
|
92
|
+
): SelectionMetrics {
|
|
93
|
+
let depth = 0
|
|
91
94
|
let aliases = 0
|
|
92
95
|
let complexity = 0
|
|
93
96
|
for (const selection of selections) {
|
|
97
|
+
let nested: SelectionMetrics
|
|
94
98
|
if (selection.kind === Kind.FIELD) {
|
|
95
99
|
complexity += 1
|
|
96
100
|
if (selection.alias !== undefined) aliases += 1
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
maxDepth = Math.max(maxDepth, depth + 1)
|
|
109
|
-
}
|
|
110
|
-
continue
|
|
101
|
+
nested =
|
|
102
|
+
selection.selectionSet === undefined
|
|
103
|
+
? NO_SELECTIONS
|
|
104
|
+
: selectionsMetrics(selection.selectionSet.selections, fragment)
|
|
105
|
+
depth = Math.max(depth, nested.depth + 1)
|
|
106
|
+
} else {
|
|
107
|
+
nested =
|
|
108
|
+
selection.kind === Kind.INLINE_FRAGMENT
|
|
109
|
+
? selectionsMetrics(selection.selectionSet.selections, fragment)
|
|
110
|
+
: fragment(selection.name.value)
|
|
111
|
+
depth = Math.max(depth, nested.depth)
|
|
111
112
|
}
|
|
112
|
-
if (selection.kind === Kind.INLINE_FRAGMENT) {
|
|
113
|
-
const nested = selectionsMetrics(
|
|
114
|
-
selection.selectionSet.selections,
|
|
115
|
-
fragments,
|
|
116
|
-
depth,
|
|
117
|
-
activeFragments,
|
|
118
|
-
)
|
|
119
|
-
maxDepth = Math.max(maxDepth, nested.depth)
|
|
120
|
-
aliases += nested.aliases
|
|
121
|
-
complexity += nested.complexity
|
|
122
|
-
continue
|
|
123
|
-
}
|
|
124
|
-
const nestedSelections = fragments.get(selection.name.value)
|
|
125
|
-
if (nestedSelections === undefined || activeFragments.has(selection.name.value)) continue
|
|
126
|
-
const nextActive = new Set(activeFragments)
|
|
127
|
-
nextActive.add(selection.name.value)
|
|
128
|
-
const nested = selectionsMetrics(nestedSelections, fragments, depth, nextActive)
|
|
129
|
-
maxDepth = Math.max(maxDepth, nested.depth)
|
|
130
113
|
aliases += nested.aliases
|
|
131
114
|
complexity += nested.complexity
|
|
132
115
|
}
|
|
133
|
-
return { depth
|
|
116
|
+
return { depth, aliases, complexity }
|
|
134
117
|
}
|
|
135
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Each fragment is measured once and reused at every spread. Expanding spreads in place costs a visit
|
|
121
|
+
* per expanded field, and a document whose fragments each spread the one before twice turns n short
|
|
122
|
+
* lines into 2^n visits, all of it synchronous before any limit can refuse the document.
|
|
123
|
+
*/
|
|
136
124
|
export function documentMetrics(document: DocumentNode): GraphqlDocumentMetrics {
|
|
137
|
-
const
|
|
125
|
+
const definitions = new Map<string, readonly SelectionNode[]>()
|
|
138
126
|
for (const definition of document.definitions) {
|
|
139
127
|
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
|
|
140
|
-
|
|
128
|
+
definitions.set(definition.name.value, definition.selectionSet.selections)
|
|
141
129
|
}
|
|
142
130
|
}
|
|
131
|
+
const measured = new Map<string, SelectionMetrics>()
|
|
132
|
+
const measuring = new Set<string>()
|
|
133
|
+
const fragment = (name: string): SelectionMetrics => {
|
|
134
|
+
const known = measured.get(name)
|
|
135
|
+
if (known !== undefined) return known
|
|
136
|
+
const selections = definitions.get(name)
|
|
137
|
+
// An unknown fragment and a cycle back into one being measured, both refused by validation,
|
|
138
|
+
// add nothing.
|
|
139
|
+
if (selections === undefined || measuring.has(name)) return NO_SELECTIONS
|
|
140
|
+
measuring.add(name)
|
|
141
|
+
const metrics = selectionsMetrics(selections, fragment)
|
|
142
|
+
measuring.delete(name)
|
|
143
|
+
measured.set(name, metrics)
|
|
144
|
+
return metrics
|
|
145
|
+
}
|
|
143
146
|
let operations = 0
|
|
144
147
|
let depth = 0
|
|
145
148
|
let aliases = 0
|
|
@@ -147,7 +150,7 @@ export function documentMetrics(document: DocumentNode): GraphqlDocumentMetrics
|
|
|
147
150
|
for (const definition of document.definitions) {
|
|
148
151
|
if (definition.kind !== Kind.OPERATION_DEFINITION) continue
|
|
149
152
|
operations += 1
|
|
150
|
-
const metrics = selectionsMetrics(definition.selectionSet.selections,
|
|
153
|
+
const metrics = selectionsMetrics(definition.selectionSet.selections, fragment)
|
|
151
154
|
depth = Math.max(depth, metrics.depth)
|
|
152
155
|
aliases += metrics.aliases
|
|
153
156
|
complexity += metrics.complexity
|
package/src/ws.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import type { NifraWebSocket, WebSocketContext, WebSocketHandler } from "@nifrajs/core/ws"
|
|
17
17
|
import {
|
|
18
18
|
type DocumentNode,
|
|
19
|
+
type ExecutionResult,
|
|
19
20
|
execute,
|
|
20
21
|
GraphQLError,
|
|
21
22
|
type GraphQLSchema,
|
|
@@ -149,10 +150,18 @@ function maskWsError(error: unknown): unknown {
|
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
/**
|
|
154
|
+
* A frame graphql-ws sends, execution errors masked. A `complete` or `error` frame ends its operation,
|
|
155
|
+
* so the id stops counting against `maxSubscriptions`: a client is not expected to complete an
|
|
156
|
+
* operation the server already ended.
|
|
157
|
+
*/
|
|
158
|
+
function outboundFrame(message: string, subscriptions: Set<string>): string {
|
|
153
159
|
try {
|
|
154
160
|
const parsed = recordOf(JSON.parse(message))
|
|
155
161
|
if (parsed === undefined) return message
|
|
162
|
+
if ((parsed.type === "complete" || parsed.type === "error") && typeof parsed.id === "string") {
|
|
163
|
+
subscriptions.delete(parsed.id)
|
|
164
|
+
}
|
|
156
165
|
if (parsed.type === "next") {
|
|
157
166
|
const payload = recordOf(parsed.payload)
|
|
158
167
|
if (payload !== undefined && Array.isArray(payload.errors)) {
|
|
@@ -171,11 +180,21 @@ function maskWsFrame(message: string): string {
|
|
|
171
180
|
return message
|
|
172
181
|
}
|
|
173
182
|
|
|
174
|
-
|
|
183
|
+
/**
|
|
184
|
+
* A timeout is that operation's error result. graphql-ws does not catch a rejected execute or
|
|
185
|
+
* subscribe, so a rejection would end the whole socket instead of the one operation.
|
|
186
|
+
*/
|
|
187
|
+
async function withWsTimeout<T>(
|
|
188
|
+
work: T | Promise<T>,
|
|
189
|
+
timeoutMs: number,
|
|
190
|
+
): Promise<T | ExecutionResult> {
|
|
175
191
|
if (!(work instanceof Promise) || timeoutMs === 0) return work
|
|
176
192
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
177
|
-
const timeout = new Promise<
|
|
178
|
-
timer = setTimeout(
|
|
193
|
+
const timeout = new Promise<ExecutionResult>((resolve) => {
|
|
194
|
+
timer = setTimeout(
|
|
195
|
+
() => resolve({ errors: [new GraphQLError("Execution timed out.")] }),
|
|
196
|
+
timeoutMs,
|
|
197
|
+
)
|
|
179
198
|
})
|
|
180
199
|
try {
|
|
181
200
|
return await Promise.race([work, timeout])
|
|
@@ -220,10 +239,14 @@ export function graphqlWebSocket<Context extends Record<string, unknown> = Recor
|
|
|
220
239
|
subscribe: subscribeWithTimeout,
|
|
221
240
|
onConnect: options.onConnect
|
|
222
241
|
? async (ctx: GraphqlWsContext<Record<string, unknown>, { request: Request }>) => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
242
|
+
try {
|
|
243
|
+
return await options.onConnect?.(
|
|
244
|
+
ctx.connectionParams as ConnectionInitMessage["payload"],
|
|
245
|
+
ctx.extra.request,
|
|
246
|
+
)
|
|
247
|
+
} catch {
|
|
248
|
+
return false
|
|
249
|
+
}
|
|
227
250
|
}
|
|
228
251
|
: undefined,
|
|
229
252
|
context: options.context
|
|
@@ -245,7 +268,7 @@ export function graphqlWebSocket<Context extends Record<string, unknown> = Recor
|
|
|
245
268
|
{
|
|
246
269
|
protocol,
|
|
247
270
|
send: async (data: string) => {
|
|
248
|
-
ws.send(
|
|
271
|
+
ws.send(outboundFrame(data, ws.data.subscriptions))
|
|
249
272
|
},
|
|
250
273
|
close: (code: number, reason: string) => {
|
|
251
274
|
ws.close(code, reason)
|
|
@@ -257,7 +280,15 @@ export function graphqlWebSocket<Context extends Record<string, unknown> = Recor
|
|
|
257
280
|
{ request: ws.data.request },
|
|
258
281
|
)
|
|
259
282
|
ws.data.closed = closed
|
|
260
|
-
ws.data.dispatch = (message: string) =>
|
|
283
|
+
ws.data.dispatch = async (message: string) => {
|
|
284
|
+
try {
|
|
285
|
+
await onFrame?.(message)
|
|
286
|
+
} catch {
|
|
287
|
+
// graphql-ws leaves a failed frame (a throwing context builder, say) to its socket adapter.
|
|
288
|
+
// Unhandled, the rejection would end a Bun or Node process; the protocol closes one socket.
|
|
289
|
+
ws.close(4500, "Internal server error")
|
|
290
|
+
}
|
|
291
|
+
}
|
|
261
292
|
},
|
|
262
293
|
|
|
263
294
|
message(ws: NifraWebSocket<GraphqlWsConnection>, data: string | Uint8Array): void {
|
|
@@ -302,7 +333,8 @@ export function graphqlWebSocket<Context extends Record<string, unknown> = Recor
|
|
|
302
333
|
|
|
303
334
|
close(ws: NifraWebSocket<GraphqlWsConnection>, code: number, reason: string): void {
|
|
304
335
|
ws.data.subscriptions.clear()
|
|
305
|
-
|
|
336
|
+
// The socket is gone: a failing iterator return() or onClose hook has no one left to answer.
|
|
337
|
+
void Promise.resolve(ws.data.closed?.(code, reason)).catch(() => {})
|
|
306
338
|
},
|
|
307
339
|
}
|
|
308
340
|
}
|