grafast 0.1.1-beta.0 → 0.1.1-beta.2

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.
Files changed (81) hide show
  1. package/README.md +1 -2
  2. package/dist/args.js +62 -0
  3. package/dist/assert.js +24 -0
  4. package/dist/bucket.js +4 -0
  5. package/dist/config.js +99 -0
  6. package/dist/constraints.js +72 -0
  7. package/dist/deferred.js +25 -0
  8. package/dist/dev.js +10 -0
  9. package/dist/engine/LayerPlan.js +536 -0
  10. package/dist/engine/OperationPlan.js +2543 -0
  11. package/dist/engine/OutputPlan.js +1059 -0
  12. package/dist/engine/StepTracker.js +524 -0
  13. package/dist/engine/executeBucket.js +700 -0
  14. package/dist/engine/executeOutputPlan.js +21 -0
  15. package/dist/engine/lib/withGlobalLayerPlan.js +36 -0
  16. package/dist/envelop.js +73 -5
  17. package/dist/error.js +62 -0
  18. package/dist/establishOperationPlan.js +186 -0
  19. package/dist/execute.js +80 -0
  20. package/dist/exportAs.js +57 -0
  21. package/dist/global.js +13 -0
  22. package/dist/grafastGraphql.js +133 -0
  23. package/dist/grafastPrint.js +177 -0
  24. package/dist/graphqlCollectFields.js +217 -0
  25. package/dist/graphqlMergeSelectionSets.js +60 -0
  26. package/dist/index.d.ts +161 -2
  27. package/dist/index.js +255 -499
  28. package/dist/input.js +159 -0
  29. package/dist/inspect.js +20 -0
  30. package/dist/interfaces.d.ts +3 -160
  31. package/dist/interfaces.js +63 -0
  32. package/dist/makeGrafastSchema.js +174 -0
  33. package/dist/mermaid.js +290 -7
  34. package/dist/operationPlan-input.js +449 -0
  35. package/dist/planJSONInterfaces.js +3 -0
  36. package/dist/polymorphic.js +73 -0
  37. package/dist/prepare.js +763 -0
  38. package/dist/step.js +396 -0
  39. package/dist/steps/__inputDynamicScalar.js +125 -0
  40. package/dist/steps/__inputList.js +110 -0
  41. package/dist/steps/__inputObject.js +128 -0
  42. package/dist/steps/__inputStaticLeaf.js +43 -0
  43. package/dist/steps/__item.js +50 -0
  44. package/dist/steps/__trackedValue.js +257 -0
  45. package/dist/steps/__value.js +47 -0
  46. package/dist/steps/access.js +183 -0
  47. package/dist/steps/applyTransforms.js +151 -0
  48. package/dist/steps/connection.js +315 -0
  49. package/dist/steps/constant.js +72 -0
  50. package/dist/steps/each.js +64 -0
  51. package/dist/steps/error.js +32 -0
  52. package/dist/steps/filter.js +32 -0
  53. package/dist/steps/first.js +49 -0
  54. package/dist/steps/graphqlResolver.js +260 -0
  55. package/dist/steps/groupBy.js +40 -0
  56. package/dist/steps/index.js +117 -0
  57. package/dist/steps/lambda.js +58 -0
  58. package/dist/steps/last.js +41 -0
  59. package/dist/steps/list.js +75 -0
  60. package/dist/steps/listTransform.js +185 -0
  61. package/dist/steps/listen.js +61 -0
  62. package/dist/steps/load.d.ts +4 -4
  63. package/dist/steps/load.js +310 -0
  64. package/dist/steps/node.d.ts +6 -6
  65. package/dist/steps/node.js +107 -0
  66. package/dist/steps/object.js +195 -0
  67. package/dist/steps/partitionByIndex.js +86 -0
  68. package/dist/steps/proxy.js +114 -0
  69. package/dist/steps/remapKeys.js +84 -0
  70. package/dist/steps/reverse.js +52 -0
  71. package/dist/steps/setter.js +28 -0
  72. package/dist/stringifyPayload.js +59 -0
  73. package/dist/stripAnsi.js +14 -0
  74. package/dist/subscribe.js +14 -0
  75. package/dist/thereCanBeOnlyOne.js +21 -0
  76. package/dist/timeSource.js +8 -0
  77. package/dist/utils.js +698 -0
  78. package/dist/version.d.ts +1 -1
  79. package/dist/version.js +6 -0
  80. package/package.json +9 -4
  81. package/dist/LICENSES.txt +0 -300
@@ -0,0 +1,1059 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getChildBucketAndIndex = exports.nonNullError = exports.coerceError = exports.OutputPlan = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const lru_1 = tslib_1.__importDefault(require("@graphile/lru"));
6
+ const graphql = tslib_1.__importStar(require("graphql"));
7
+ const tamedevil_1 = tslib_1.__importStar(require("tamedevil"));
8
+ const assert = tslib_1.__importStar(require("../assert.js"));
9
+ const dev_js_1 = require("../dev.js");
10
+ const error_js_1 = require("../error.js");
11
+ const index_js_1 = require("../index.js");
12
+ const inspect_js_1 = require("../inspect.js");
13
+ const interfaces_js_1 = require("../interfaces.js");
14
+ const polymorphic_js_1 = require("../polymorphic.js");
15
+ const access_js_1 = require("../steps/access.js");
16
+ const { executeSync, GraphQLBoolean, GraphQLError, GraphQLFloat, GraphQLID, GraphQLInt, GraphQLString, isObjectType, Kind, OperationTypeNode, } = graphql;
17
+ const ref_$$error = tamedevil_1.default.ref(error_js_1.$$error, "$$error");
18
+ const ref_coerceError = tamedevil_1.default.ref(coerceError, "coerceError");
19
+ const ref_nonNullError = tamedevil_1.default.ref(nonNullError, "nonNullError");
20
+ const ref_stringifyString = tamedevil_1.default.ref(tamedevil_1.stringifyString, "stringifyString");
21
+ const ref_stringifyJSON = tamedevil_1.default.ref(tamedevil_1.stringifyJSON, "stringifyJSON");
22
+ const ref_isPolymorphicData = tamedevil_1.default.ref(polymorphic_js_1.isPolymorphicData, "isPolymorphicData");
23
+ const ref_$$concreteType = tamedevil_1.default.ref(interfaces_js_1.$$concreteType, "$$concreteType");
24
+ const ref_assert = tamedevil_1.default.ref(assert, "assert");
25
+ const ref_getChildBucketAndIndex = tamedevil_1.default.ref(getChildBucketAndIndex, "getChildBucketAndIndex");
26
+ const ref_inspect = tamedevil_1.default.ref(inspect_js_1.inspect, "inspect");
27
+ const ref_$$streamMore = tamedevil_1.default.ref(interfaces_js_1.$$streamMore, "$$streamMore");
28
+ const te_String = (0, tamedevil_1.default) `String`;
29
+ const te_nullIsFineComment = (0, tamedevil_1.default) `// root/introspection, null is fine`;
30
+ const te_nullString = (0, tamedevil_1.default) `"null"`;
31
+ const te_null = (0, tamedevil_1.default) `null`;
32
+ const te_childBucket = (0, tamedevil_1.default) `childBucket`;
33
+ const te_childBucketIndex = (0, tamedevil_1.default) `childBucketIndex`;
34
+ const te_executeString = (0, tamedevil_1.default) `executeString`;
35
+ const te_execute = (0, tamedevil_1.default) `execute`;
36
+ const te_polymorphic = (0, tamedevil_1.default) `polymorphic`;
37
+ const te_letStringLbrace = (0, tamedevil_1.default) `let string = "{";`;
38
+ const te_stringPlusEquals = (0, tamedevil_1.default) `string +=`;
39
+ const te_stringPlusEqualsRbrace = (0, tamedevil_1.default) ` string += "}";\n`;
40
+ const te_stringPlusEqualsRbracket = (0, tamedevil_1.default) ` string += "]";\n`;
41
+ const te_constObjEqualsObjectCreateNull = (0, tamedevil_1.default) `const obj = Object.create(null);`;
42
+ const te_comma = (0, tamedevil_1.default) `,`;
43
+ const te_specDotLocationDetails = (0, tamedevil_1.default) `spec.locationDetails`;
44
+ const te_thisDotLocationDetails = (0, tamedevil_1.default) `this.locationDetails`;
45
+ const te_specDotOutputPlan = (0, tamedevil_1.default) `spec.outputPlan`;
46
+ const te_bucket = (0, tamedevil_1.default) `bucket`;
47
+ const te_bucketIndex = (0, tamedevil_1.default) `bucketIndex`;
48
+ const te_handleDeferred = (0, tamedevil_1.default) `
49
+ // Everything seems okay; queue any deferred payloads
50
+ for (const defer of this.deferredOutputPlans) {
51
+ root.queue.push({
52
+ root,
53
+ path: mutablePath.slice(1),
54
+ bucket,
55
+ bucketIndex,
56
+ outputPlan: defer,
57
+ label: defer.type.deferLabel,
58
+ });
59
+ }
60
+ `;
61
+ const te_string = (0, tamedevil_1.default) `string`;
62
+ const te_object = (0, tamedevil_1.default) `object`;
63
+ const te_obj = (0, tamedevil_1.default) `obj`;
64
+ const te_data = (0, tamedevil_1.default) `data`;
65
+ const te_commonErrorHandler = tamedevil_1.default.ref(commonErrorHandler, "handleError");
66
+ const te_questionDot = (0, tamedevil_1.default) `?.`;
67
+ const te_childBucketCBIDC = (0, tamedevil_1.default) `, childBucket, childBucketIndex, directChild`;
68
+ const te_letString = (0, tamedevil_1.default) `let string;`;
69
+ const te_constDataEqualsEmptyArray = (0, tamedevil_1.default) `const data = [];`;
70
+ const te_stringEqualsEmptyArrayString = (0, tamedevil_1.default) ` string = "[]";`;
71
+ const te_stringEqualsLeftSquareBraceString = (0, tamedevil_1.default) ` string = "[";\n`;
72
+ const te_noopComment = (0, tamedevil_1.default) ` /* noop */`;
73
+ const te_ifIGt0StrPlusEqualComma = (0, tamedevil_1.default) `\n if (i > 0) { string += ","; }\n`;
74
+ const te_dataIEquals = (0, tamedevil_1.default) `data[i] =`;
75
+ const te_childOutputPlan = (0, tamedevil_1.default) `childOutputPlan`;
76
+ const te_underscoreNonNull = (0, tamedevil_1.default) `_nonNull`;
77
+ const te_underscoreStream = (0, tamedevil_1.default) `_stream`;
78
+ /**
79
+ * Defines a way of taking a layerPlan and converting it into an output value.
80
+ *
81
+ * The result of executing an output plan will be the following:
82
+ *
83
+ * - data?: the data for this layer, could be object, array or leaf (see OutputPlanMode)
84
+ * - errors: a list of errors that occurred (if any), including path details _within the output plan_
85
+ * - streams: a list of streams that were created
86
+ */
87
+ class OutputPlan {
88
+ constructor(layerPlan,
89
+ /**
90
+ * For root, this should always be an object.
91
+ *
92
+ * For object this could represent an object or null.
93
+ *
94
+ * For polymorphic this could represent a polymorphic object or null.
95
+ *
96
+ * For array it's the list itself (or null) and dictates the length of the result.
97
+ *
98
+ * For leaf, it's the leaf plan itself.
99
+ *
100
+ * For `introspection`, `null` it's irrelevant. Use `constant(null)` or whatever.
101
+ */
102
+ rootStep, type, locationDetails) {
103
+ this.layerPlan = layerPlan;
104
+ this.type = type;
105
+ /**
106
+ * Appended to the root step when accessed to avoid the need for AccessSteps
107
+ */
108
+ this.processRoot = null;
109
+ /**
110
+ * For root/object output plans, the keys to set on the resulting object
111
+ * grouped by the concrete object type name.
112
+ *
113
+ * IMPORTANT: the order of these keys is significant, they MUST match the
114
+ * order in the request otherwise we break GraphQL spec compliance!
115
+ */
116
+ this.keys = Object.create(null);
117
+ /**
118
+ * For list output plans, the output plan that describes the list children.
119
+ */
120
+ this.child = null;
121
+ this.childIsNonNull = false;
122
+ /**
123
+ * For object output plan types only.
124
+ */
125
+ this.deferredOutputPlans = [];
126
+ this.locationDetails = locationDetails;
127
+ this.rootStep = rootStep;
128
+ layerPlan.operationPlan.stepTracker.addOutputPlan(this);
129
+ this.childByTypeName =
130
+ this.type.mode === "polymorphic" ? Object.create(null) : undefined;
131
+ }
132
+ print() {
133
+ const type = this.type;
134
+ switch (type.mode) {
135
+ case "root":
136
+ case "object": {
137
+ return `${this.toString()}\n${Object.entries(this.keys)
138
+ .map(([fieldName, val]) => {
139
+ return `.${fieldName}: ${val.type === "__typename" ? "" : val.isNonNull ? "!" : "?"}${val.type === "__typename"
140
+ ? `__typename(${type.typeName})`
141
+ : val.outputPlan.print().replace(/\n/g, "\n ")}`;
142
+ })
143
+ .join("\n")}`;
144
+ }
145
+ case "polymorphic": {
146
+ return `${this.toString()}\n${Object.entries(this.childByTypeName)
147
+ .map(([typeName, outputPlan]) => {
148
+ return `? ${typeName}: ${outputPlan
149
+ .print()
150
+ .replace(/\n/g, "\n ")}`;
151
+ })
152
+ .join("\n")}`;
153
+ }
154
+ case "array": {
155
+ return `${this.toString()}\n ${this.childIsNonNull ? "!" : "?"}${this.child.print().replace(/\n/g, "\n ")}`;
156
+ }
157
+ default: {
158
+ return this.toString();
159
+ }
160
+ }
161
+ }
162
+ toString() {
163
+ return `OutputPlan<${this.type.mode}∈${this.layerPlan.id}!${this.rootStep.id}>`;
164
+ }
165
+ addChild(type, key, child) {
166
+ if (this.type.mode === "root" || this.type.mode === "object") {
167
+ if (dev_js_1.isDev) {
168
+ if (typeof key !== "string") {
169
+ throw new Error(`GrafastInternalError<7334ec50-23dc-442a-8ffa-19664c9eb79f>: Key must be provided in ${this.type.mode} OutputPlan mode`);
170
+ }
171
+ if (type == null) {
172
+ throw new Error(`GrafastInternalError<638cebef-4ec6-49f4-b681-2f390fb1c0fc>: Type must be provided in ${this.type.mode} OutputPlan mode.`);
173
+ }
174
+ if (!isObjectType(type)) {
175
+ throw new Error(`GrafastInternalError<eaa87576-1d50-49be-990a-345a9b57b998>: Type must provided in ${this.type.mode} OutputPlan mode must be an object type, instead saw '${type}'.`);
176
+ }
177
+ assert.ok(["root", "object"].includes(this.type.mode), "Can only addChild on root/object output plans");
178
+ if (this.keys[key] !== undefined) {
179
+ throw new Error(`GrafastInternalError<5ceecb19-8c2c-4797-9be5-9be1b207fa45>: child already set for key '${key}'`);
180
+ }
181
+ }
182
+ this.keys[key] =
183
+ child.type === "outputPlan"
184
+ ? {
185
+ ...child,
186
+ layerPlanId: child.outputPlan.layerPlan.id,
187
+ }
188
+ : child;
189
+ }
190
+ else if (this.type.mode === "array") {
191
+ if (dev_js_1.isDev) {
192
+ if (key != null) {
193
+ throw new Error(`GrafastInternalError<7de67325-a02f-4619-b118-61bb2d84f33b>: Key must not be provided in ${this.type.mode} OutputPlan mode`);
194
+ }
195
+ assert.ok(type == null, "Array should not specify type");
196
+ if (this.child !== null) {
197
+ throw new Error(`GrafastInternalError<07059d9d-a47d-441f-b834-683cca1d856a>: child already set`);
198
+ }
199
+ }
200
+ if (child.type === "outputPlan") {
201
+ this.child = child.outputPlan;
202
+ this.childIsNonNull = child.isNonNull;
203
+ }
204
+ else {
205
+ throw new Error(`GrafastInternalError<7525c854-9145-4c6d-8d60-79c14f040519>: Array child must be an outputPlan`);
206
+ }
207
+ }
208
+ else if (this.type.mode === "polymorphic") {
209
+ if (dev_js_1.isDev) {
210
+ assert.ok(type && this.type.typeNames.includes(type.name), "GrafastInternalError<566a34ac-1138-4dbf-943e-f704819431dd>: polymorphic output plan can only addChild for a matching type");
211
+ assert.strictEqual(key, null, "GrafastInternalError<4346ebda-a02d-4489-b767-7a6d621a73c7>: addChild for polymorphic OutputPlan should not specify a key");
212
+ assert.ok(child.type === "outputPlan", "GrafastInternalError<b29285da-fb07-4943-9038-708edc785041>: polymorphic OutputPlan child must be an outputPlan");
213
+ assert.ok(child.outputPlan.type.mode === "object", "GrafastInternalError<203469c6-4bfa-4cd1-ae82-cc5d0132ca16>: polymorphic OutputPlan child must be an object outputPlan");
214
+ }
215
+ this.childByTypeName[type.name] = child.outputPlan;
216
+ }
217
+ else {
218
+ throw new Error(`GrafastInternalError<5667df5f-30b7-48d3-be3f-a0065ed9c05c>: Doesn't make sense to set a child in mode '${this.type.mode}'`);
219
+ }
220
+ }
221
+ getLayerPlans(layerPlans = new Set()) {
222
+ // Find all the layerPlans referenced
223
+ layerPlans.add(this.layerPlan);
224
+ if (this.child !== null) {
225
+ if (this.child.layerPlan != this.layerPlan) {
226
+ this.child.getLayerPlans(layerPlans);
227
+ }
228
+ else {
229
+ throw new Error("GrafastInternalError<4013e05f-b8ed-41ea-a869-204232d02763>: how could the child be in the same layer?");
230
+ }
231
+ }
232
+ for (const key in this.keys) {
233
+ const spec = this.keys[key];
234
+ if (spec.type === "outputPlan") {
235
+ if (spec.outputPlan.layerPlan !== this.layerPlan) {
236
+ spec.outputPlan.getLayerPlans(layerPlans);
237
+ layerPlans.add(spec.outputPlan.layerPlan);
238
+ }
239
+ }
240
+ }
241
+ return layerPlans;
242
+ }
243
+ makeNextStepByLayerPlan() {
244
+ const layerPlans = this.getLayerPlans();
245
+ const map = Object.create(null);
246
+ for (const layerPlan of layerPlans) {
247
+ map[layerPlan.id] = [];
248
+ }
249
+ return map;
250
+ }
251
+ // This gets replaced with a mode-specific executor
252
+ execute(_root,
253
+ // By just reusing the same path over and over we don't need to allocate
254
+ // more memory for more arrays; but we must be _incredibly_ careful to
255
+ // ensure any changes to it are reversed.
256
+ _mutablePath, _bucket, _bucketIndex) {
257
+ throw new Error(`OutputPlan.execute has yet to be built!`);
258
+ }
259
+ // This gets replaced with a mode-specific executor
260
+ executeString(_root,
261
+ // By just reusing the same path over and over we don't need to allocate
262
+ // more memory for more arrays; but we must be _incredibly_ careful to
263
+ // ensure any changes to it are reversed.
264
+ _mutablePath, _bucket, _bucketIndex) {
265
+ throw new Error(`OutputPlan.executeString has yet to be built!`);
266
+ }
267
+ optimize() {
268
+ const $root = this.layerPlan.operationPlan.dangerouslyGetStep(this.rootStep.id);
269
+ if ($root instanceof index_js_1.AccessStep && $root.fallback === undefined) {
270
+ const expressionDetails = $root.unbatchedExecute[access_js_1.expressionSymbol];
271
+ if (expressionDetails !== undefined) {
272
+ // @ts-ignore
273
+ const $parent = $root.getDep(0);
274
+ this.layerPlan.operationPlan.stepTracker.setOutputPlanRootStep(this, $parent);
275
+ const [path, fallback] = expressionDetails;
276
+ withFastExpression(path, fallback, (fn) => {
277
+ this.processRoot = fn;
278
+ });
279
+ }
280
+ }
281
+ }
282
+ finalize() {
283
+ // Build the executor
284
+ switch (this.type.mode) {
285
+ case "null": {
286
+ this.execute = nullExecutor;
287
+ this.executeString = nullExecutorString;
288
+ break;
289
+ }
290
+ case "leaf": {
291
+ this.execute = leafExecutor;
292
+ if (this.type.graphqlType.serialize === GraphQLID.serialize ||
293
+ this.type.graphqlType.serialize === GraphQLString.serialize) {
294
+ // String types
295
+ this.executeString = stringLeafExecutorString;
296
+ }
297
+ else if (this.type.graphqlType.serialize === GraphQLInt.serialize) {
298
+ this.executeString = intLeafExecutorString;
299
+ }
300
+ else if (this.type.graphqlType.serialize === GraphQLFloat.serialize) {
301
+ this.executeString = floatLeafExecutorString;
302
+ }
303
+ else if (this.type.graphqlType.serialize === GraphQLBoolean.serialize) {
304
+ // Boolean type
305
+ this.executeString = booleanLeafExecutorString;
306
+ }
307
+ else {
308
+ // PERF: we could probably optimize enums too
309
+ this.executeString = leafExecutorString;
310
+ }
311
+ break;
312
+ }
313
+ case "introspection": {
314
+ this.execute = introspectionExecutor;
315
+ this.executeString = introspectionExecutorString;
316
+ break;
317
+ }
318
+ case "polymorphic": {
319
+ this.execute = polymorphicExecutor;
320
+ this.executeString = polymorphicExecutorString;
321
+ break;
322
+ }
323
+ case "array": {
324
+ if (!this.child) {
325
+ throw new Error("GrafastInternalError<48fabdc8-ce84-45ec-ac20-35a2af9098e0>: No child output plan for list bucket?");
326
+ }
327
+ const childIsNonNull = this.childIsNonNull;
328
+ let directLayerPlanChild = this.child.layerPlan;
329
+ while (directLayerPlanChild.parentLayerPlan !== this.layerPlan) {
330
+ const parent = directLayerPlanChild.parentLayerPlan;
331
+ if (!parent) {
332
+ throw new Error(`GrafastInternalError<d43e06d8-c533-4e7b-b3e7-af399f19c83f>: Invalid heirarchy - could not find direct layerPlan child of ${this}`);
333
+ }
334
+ directLayerPlanChild = parent;
335
+ }
336
+ const canStream = directLayerPlanChild.reason.type === "listItem" &&
337
+ !!directLayerPlanChild.reason.stream;
338
+ if (childIsNonNull) {
339
+ if (canStream) {
340
+ this.execute = arrayExecutor_nonNullable_streaming;
341
+ this.executeString = arrayExecutorString_nonNullable_streaming;
342
+ }
343
+ else {
344
+ this.execute = arrayExecutor_nonNullable;
345
+ this.executeString = arrayExecutorString_nonNullable;
346
+ }
347
+ }
348
+ else {
349
+ if (canStream) {
350
+ this.execute = arrayExecutor_nullable_streaming;
351
+ this.executeString = arrayExecutorString_nullable_streaming;
352
+ }
353
+ else {
354
+ this.execute = arrayExecutor_nullable;
355
+ this.executeString = arrayExecutorString_nullable;
356
+ }
357
+ }
358
+ break;
359
+ }
360
+ case "root":
361
+ case "object": {
362
+ const type = this.type;
363
+ const digestFieldTypes = Object.create(null);
364
+ for (const [responseKey, spec] of Object.entries(this.keys)) {
365
+ digestFieldTypes[responseKey] = {
366
+ fieldType: spec.type === "__typename"
367
+ ? "__typename"
368
+ : spec.isNonNull
369
+ ? "outputPlan!"
370
+ : "outputPlan?",
371
+ sameBucket: spec.type === "__typename" ||
372
+ spec.outputPlan.layerPlan.id === this.layerPlan.id,
373
+ };
374
+ }
375
+ makeObjectExecutor(type.typeName, digestFieldTypes, this.deferredOutputPlans.length > 0, type.mode === "root", false, (fn) => {
376
+ this.execute = fn;
377
+ });
378
+ makeObjectExecutor(type.typeName, digestFieldTypes, this.deferredOutputPlans.length > 0, type.mode === "root", true, (fn) => {
379
+ this.executeString = fn;
380
+ });
381
+ break;
382
+ }
383
+ default: {
384
+ const never = this.type;
385
+ throw new Error(`GrafastInternalError<e88531b2-d9af-4d3a-8cd5-e9f034324341>: Could not build executor for OutputPlan with type ${(0, inspect_js_1.inspect)(never)}}`);
386
+ }
387
+ }
388
+ }
389
+ }
390
+ exports.OutputPlan = OutputPlan;
391
+ function coerceError(error, locationDetails, path) {
392
+ // Ensure it's a GraphQL error
393
+ if (error instanceof GraphQLError) {
394
+ if (error.path !== undefined) {
395
+ return error;
396
+ }
397
+ else {
398
+ return new GraphQLError(error.message, locationDetails.node, null, null, path, error.originalError ?? error, error.extensions);
399
+ }
400
+ }
401
+ else {
402
+ return new GraphQLError(error.message, locationDetails.node, null, null, path, error, null);
403
+ }
404
+ }
405
+ exports.coerceError = coerceError;
406
+ function nonNullError(locationDetails, path) {
407
+ const { parentTypeName, fieldName, node } = locationDetails;
408
+ if (!parentTypeName || !fieldName) {
409
+ return new GraphQLError(`GrafastInternalError<a3706bba-4f88-4643-8a47-2fe2eaaadbea>: null bubbled to root`, node, null, null, path, null, null);
410
+ }
411
+ return new GraphQLError(`Cannot return null for non-nullable field ${parentTypeName}.${fieldName}.`, node, null, null, path, null, null);
412
+ }
413
+ exports.nonNullError = nonNullError;
414
+ /**
415
+ * We're currently running OutputPlan `outputPlan` in bucket `bucket` at index
416
+ * `bucketIndex`. If this is an array OutputPlan then we're looping over the
417
+ * entries in our list and we're currently at index `inArrayIndex` (otherwise
418
+ * this is null).
419
+ *
420
+ * Now we want to run `childOutputPlan`, but to do so we need to find the
421
+ * related `childBucket` and `childBucketIndex`.
422
+ *
423
+ * @internal
424
+ */
425
+ function getChildBucketAndIndex(childOutputPlan, outputPlan, bucket, bucketIndex, arrayIndex = null) {
426
+ if ((arrayIndex == null) ===
427
+ (outputPlan != null && outputPlan.type.mode === "array")) {
428
+ throw new Error("GrafastInternalError<83d0e3cc-7eec-4185-85b4-846540288162>: arrayIndex must be supplied iff outputPlan is an array");
429
+ }
430
+ if (outputPlan && childOutputPlan.layerPlan === bucket.layerPlan) {
431
+ // Same layer; straightforward
432
+ return [bucket, bucketIndex];
433
+ }
434
+ const reversePath = [childOutputPlan.layerPlan];
435
+ let current = childOutputPlan.layerPlan;
436
+ while (!bucket.children[current.id]) {
437
+ current = current.parentLayerPlan;
438
+ if (!current) {
439
+ return null;
440
+ }
441
+ reversePath.push(current);
442
+ }
443
+ let currentBucket = bucket;
444
+ let currentIndex = bucketIndex;
445
+ for (let l = reversePath.length, i = l - 1; i >= 0; i--) {
446
+ const layerPlan = reversePath[i];
447
+ const child = currentBucket.children[layerPlan.id];
448
+ if (!child) {
449
+ return null;
450
+ }
451
+ const out = child.map.get(currentIndex);
452
+ if (out == null) {
453
+ return null;
454
+ }
455
+ /*
456
+ * TEST: I think this '|| i !== l - 1' check is saying that an array can
457
+ * only occur at the furthest ancestor and everything since then must be
458
+ * non-array. Presumably in the case of nested arrays there would be an
459
+ * intermediary bucket, hence why this check is allowed, but that should be
460
+ * tested. Also, are there any confounding factors when it comes to steps
461
+ * themselves using arrays for object values - for example pgSelectSingle is
462
+ * represented by an array (tuple), but that doesn't make it a list so it should
463
+ * be fine. Use tests to validate this is all fine.
464
+ */
465
+ if (arrayIndex == null || i !== l - 1) {
466
+ if (Array.isArray(out)) {
467
+ throw new Error("GrafastInternalError<db189d32-bf8f-4e58-b55f-5c5ac3bb2381>: Was expecting an arrayIndex, but none was provided");
468
+ }
469
+ currentBucket = child.bucket;
470
+ currentIndex = out;
471
+ }
472
+ else {
473
+ if (!Array.isArray(out)) {
474
+ throw new Error(`GrafastInternalError<8190d09f-dc75-46ec-8162-b20ad516de41>: Cannot access array index in non-array ${(0, inspect_js_1.inspect)(out)}`);
475
+ }
476
+ if (!(out.length > arrayIndex)) {
477
+ throw new Error(`GrafastInternalError<1f596c22-368b-4d0d-94df-fb3df632b064>: Attempted to retrieve array index '${arrayIndex}' which is out of bounds of array with length '${out.length}'`);
478
+ }
479
+ currentBucket = child.bucket;
480
+ currentIndex = out[arrayIndex];
481
+ }
482
+ }
483
+ if (currentIndex == null) {
484
+ return null;
485
+ }
486
+ return [currentBucket, currentIndex];
487
+ }
488
+ exports.getChildBucketAndIndex = getChildBucketAndIndex;
489
+ function makeExecutorExpression(options) {
490
+ const { inner, nameExtra, asString, skipNullHandling = false, preamble = tamedevil_1.default.blank, } = options;
491
+ return (0, tamedevil_1.default) `\
492
+ (function compiledOutputPlan${asString ? te_String : tamedevil_1.default.blank}_${nameExtra}(
493
+ root,
494
+ mutablePath,
495
+ bucket,
496
+ bucketIndex,
497
+ rawBucketRootValue = bucket.store.get(this.rootStep.id)[bucketIndex]
498
+ ) {
499
+ const bucketRootValue = this.processRoot !== null ? this.processRoot(rawBucketRootValue) : rawBucketRootValue;
500
+ ${preamble}\
501
+ ${skipNullHandling
502
+ ? dev_js_1.isDev
503
+ ? te_nullIsFineComment
504
+ : tamedevil_1.default.blank
505
+ : (0, tamedevil_1.default) `if (bucketRootValue == null) return ${asString ? te_nullString : te_null};`}
506
+ if (bucketRootValue${skipNullHandling ? te_questionDot : tamedevil_1.default.blank}[${ref_$$error}]) {
507
+ throw ${ref_coerceError}(bucketRootValue.originalError, this.locationDetails, mutablePath.slice(1));
508
+ }
509
+ ${inner}
510
+ })`;
511
+ }
512
+ function makeExecutor(options, callback) {
513
+ const expression = makeExecutorExpression(options);
514
+ if (callback !== undefined) {
515
+ tamedevil_1.default.runInBatch(expression, callback);
516
+ }
517
+ else {
518
+ return tamedevil_1.default.run((0, tamedevil_1.default) `return ${expression}`);
519
+ }
520
+ }
521
+ function makeExecuteChildPlanCode(setTargetOrReturn, locationDetails, childOutputPlan, isNonNull, asString, childBucket = te_childBucket, childBucketIndex = te_childBucketIndex) {
522
+ const te_childOutputPlanExecute = (0, tamedevil_1.default) `${childOutputPlan}.${asString ? te_executeString : te_execute}(root, mutablePath, ${childBucket}, ${childBucketIndex}, ${childBucket}.rootStep === this.rootStep ? rawBucketRootValue : undefined)`;
523
+ // This is the code that changes based on if the field is nullable or not
524
+ if (isNonNull) {
525
+ // No need to catch error
526
+ return (0, tamedevil_1.default) `\
527
+ ${childBucket === te_bucket
528
+ ? tamedevil_1.default.blank
529
+ : (0, tamedevil_1.default) `if (${childBucket} == null) {
530
+ throw ${ref_nonNullError}(${locationDetails}, mutablePath.slice(1));
531
+ }
532
+ `}fieldResult = ${te_childOutputPlanExecute};
533
+ if (fieldResult == ${asString ? te_nullString : te_null}) {
534
+ throw ${ref_nonNullError}(${locationDetails}, mutablePath.slice(1));
535
+ }
536
+ ${setTargetOrReturn} fieldResult;`;
537
+ }
538
+ else {
539
+ // Need to catch error and set null
540
+ return (0, tamedevil_1.default) `\
541
+ {
542
+ const streamCount = root.streams.length;
543
+ const queueCount = root.queue.length;
544
+ try {
545
+ ${setTargetOrReturn} ${childBucket === te_bucket
546
+ ? tamedevil_1.default.blank
547
+ : (0, tamedevil_1.default) `${childBucket} == null ? ${asString ? te_nullString : te_null} : `}${te_childOutputPlanExecute};
548
+ } catch (e) {
549
+ ${te_commonErrorHandler}(e, ${locationDetails}, mutablePath, mutablePathIndex, root, streamCount, queueCount);
550
+ ${setTargetOrReturn} ${asString ? te_nullString : te_null};
551
+ }
552
+ }`;
553
+ }
554
+ }
555
+ function commonErrorHandler(e, locationDetails, mutablePath, mutablePathIndex, root, streamCount, queueCount) {
556
+ if (root.streams.length > streamCount) {
557
+ root.streams.splice(streamCount);
558
+ }
559
+ if (root.queue.length > queueCount) {
560
+ root.queue.splice(queueCount);
561
+ }
562
+ const error = coerceError(e, locationDetails, mutablePath.slice(1));
563
+ const pathLengthTarget = mutablePathIndex + 1;
564
+ const overSize = mutablePath.length - pathLengthTarget;
565
+ if (overSize > 0) {
566
+ mutablePath.splice(pathLengthTarget, overSize);
567
+ }
568
+ root.errors.push(error);
569
+ }
570
+ /*
571
+ * Below here we create the executors that can be shared across many different
572
+ * OutputPlans without having to be recreated (thus saving significant memory,
573
+ * and increasing the probability of optimization).
574
+ */
575
+ const nullExecutor = makeExecutor({
576
+ inner: tamedevil_1.default.cache ` return null;`,
577
+ nameExtra: tamedevil_1.default.cache `null`,
578
+ asString: false,
579
+ });
580
+ const nullExecutorString = makeExecutor({
581
+ inner: tamedevil_1.default.cache ` return "null";`,
582
+ nameExtra: tamedevil_1.default.cache `null`,
583
+ asString: true,
584
+ });
585
+ /* This is what leafExecutor should use if insideGraphQL (which isn't currently
586
+ * supported)
587
+ `\
588
+ if (root.insideGraphQL) {
589
+ // Don't serialize to avoid the double serialization problem
590
+ return bucketRootValue;
591
+ } else {
592
+ return this.type.graphqlType.serialize(bucketRootValue);
593
+ }
594
+ ` */
595
+ const leafExecutor = makeExecutor({
596
+ inner: (0, tamedevil_1.default) `\
597
+ return this.type.graphqlType.serialize(bucketRootValue);
598
+ `,
599
+ nameExtra: tamedevil_1.default.cache `leaf`,
600
+ asString: false,
601
+ });
602
+ const leafExecutorString = makeExecutor({
603
+ inner: (0, tamedevil_1.default) `\
604
+ return ${ref_stringifyJSON}(this.type.graphqlType.serialize(bucketRootValue));
605
+ `,
606
+ nameExtra: tamedevil_1.default.cache `leaf`,
607
+ asString: true,
608
+ });
609
+ const booleanLeafExecutorString = makeExecutor({
610
+ inner: (0, tamedevil_1.default) `\
611
+ const val = this.type.graphqlType.serialize(bucketRootValue);
612
+ return val === true ? 'true' : 'false';
613
+ `,
614
+ nameExtra: tamedevil_1.default.cache `booleanLeaf`,
615
+ asString: true,
616
+ skipNullHandling: false,
617
+ preamble: (0, tamedevil_1.default) `\
618
+ if (bucketRootValue === true) return 'true';
619
+ if (bucketRootValue === false) return 'false';
620
+ `,
621
+ });
622
+ const intLeafExecutorString = makeExecutor({
623
+ inner: (0, tamedevil_1.default) `\
624
+ return '' + this.type.graphqlType.serialize(bucketRootValue);
625
+ `,
626
+ nameExtra: tamedevil_1.default.cache `intLeaf`,
627
+ asString: true,
628
+ skipNullHandling: false,
629
+ // Fast check to see if number is 32 bit integer
630
+ preamble: (0, tamedevil_1.default) `\
631
+ if ((bucketRootValue | 0) === bucketRootValue) {
632
+ return '' + bucketRootValue;
633
+ }
634
+ `,
635
+ });
636
+ const floatLeafExecutorString = makeExecutor({
637
+ inner: (0, tamedevil_1.default) `\
638
+ return String(this.type.graphqlType.serialize(bucketRootValue));
639
+ `,
640
+ nameExtra: tamedevil_1.default.cache `floatLeaf`,
641
+ asString: true,
642
+ skipNullHandling: false,
643
+ preamble: (0, tamedevil_1.default) `\
644
+ if (Number.isFinite(bucketRootValue)) {
645
+ return '' + bucketRootValue;
646
+ }
647
+ `,
648
+ });
649
+ const stringLeafExecutorString = makeExecutor({
650
+ inner: (0, tamedevil_1.default) `\
651
+ return ${ref_stringifyString}(this.type.graphqlType.serialize(bucketRootValue));
652
+ `,
653
+ nameExtra: tamedevil_1.default.cache `stringLeaf`,
654
+ asString: true,
655
+ skipNullHandling: false,
656
+ preamble: (0, tamedevil_1.default) `\
657
+ if (typeof bucketRootValue === 'string') {
658
+ return ${ref_stringifyString}(bucketRootValue);
659
+ }
660
+ `,
661
+ });
662
+ // NOTE: the reference to $$concreteType here is a (temporary) optimization; it
663
+ // should be `resolveType(bucketRootValue)` but it's not worth the function
664
+ // call overhead. Longer term it should just be read directly from a different
665
+ // store.
666
+ function makePolymorphicExecutor(asString) {
667
+ return makeExecutor({
668
+ inner: (0, tamedevil_1.default) `\
669
+ ${dev_js_1.isDev
670
+ ? (0, tamedevil_1.default) `\
671
+ if (!${ref_isPolymorphicData}(bucketRootValue)) {
672
+ throw ${ref_coerceError}(
673
+ new Error(
674
+ "GrafastInternalError<db7fcda5-dc39-4568-a7ce-ee8acb88806b>: Expected polymorphic data",
675
+ ),
676
+ this.locationDetails,
677
+ mutablePath.slice(1),
678
+ );
679
+ }
680
+ `
681
+ : tamedevil_1.default.blank}\
682
+ const typeName = bucketRootValue[${ref_$$concreteType}];
683
+ const childOutputPlan = this.childByTypeName[typeName];
684
+ ${dev_js_1.isDev
685
+ ? (0, tamedevil_1.default) `{
686
+ ${ref_assert}.ok(
687
+ typeName,
688
+ "GrafastInternalError<fd3f3cf0-0789-4c74-a6cd-839c808896ed>: Could not determine concreteType for object",
689
+ );
690
+ ${ref_assert}.ok(
691
+ childOutputPlan,
692
+ \`GrafastInternalError<a46999ef-41ff-4a22-bae9-fa37ff6e5f7f>: Could not determine the OutputPlan to use for '\${typeName}' from '\${bucket.layerPlan}'\`,
693
+ );
694
+ }`
695
+ : tamedevil_1.default.blank}
696
+
697
+ const directChild = bucket.children[childOutputPlan.layerPlan.id];
698
+ if (directChild !== undefined) {
699
+ return childOutputPlan.${asString ? te_executeString : te_execute}(root, mutablePath, directChild.bucket, directChild.map.get(bucketIndex));
700
+ } else {
701
+ const c = ${ref_getChildBucketAndIndex}(
702
+ childOutputPlan,
703
+ this,
704
+ bucket,
705
+ bucketIndex,
706
+ );
707
+ if (!c) {
708
+ throw new Error("GrafastInternalError<691509d8-31fa-4cfe-a6df-dcba21bd521f>: polymorphic executor couldn't determine child bucket");
709
+ }
710
+ const [childBucket, childBucketIndex] = c;
711
+ return childOutputPlan.${asString ? te_executeString : te_execute}(root, mutablePath, childBucket, childBucketIndex);
712
+ }
713
+ `,
714
+ nameExtra: te_polymorphic,
715
+ asString,
716
+ });
717
+ }
718
+ const polymorphicExecutor = makePolymorphicExecutor(false);
719
+ const polymorphicExecutorString = makePolymorphicExecutor(true);
720
+ function makeArrayExecutor(childIsNonNull, canStream, asString) {
721
+ return makeExecutor({
722
+ inner: (0, tamedevil_1.default) `\
723
+ if (!Array.isArray(bucketRootValue)) {
724
+ console.warn(\`Hit fallback for value \${${ref_inspect}(bucketRootValue)} coercion to mode 'array'\`);
725
+ return ${asString ? te_nullString : te_null};
726
+ }
727
+
728
+ const childOutputPlan = this.child;
729
+ const l = bucketRootValue.length;
730
+ let fieldResult;
731
+ ${asString ? te_letString : te_constDataEqualsEmptyArray}
732
+ if (l === 0) {
733
+ ${asString ? te_stringEqualsEmptyArrayString : te_noopComment}
734
+ } else {
735
+ ${asString ? te_stringEqualsLeftSquareBraceString : tamedevil_1.default.blank}\
736
+ const mutablePathIndex = mutablePath.push(-1) - 1;
737
+
738
+ // Now to populate the children...
739
+ const directChild = bucket.children[childOutputPlan.layerPlan.id];
740
+ let childBucket, childBucketIndex, lookup;
741
+ if (directChild !== undefined) {
742
+ childBucket = directChild.bucket;
743
+ lookup = directChild.map.get(bucketIndex)
744
+ }
745
+ for (let i = 0; i < l; i++) {
746
+ if (directChild !== undefined) {
747
+ childBucketIndex = lookup[i];
748
+ } else {
749
+ const c = ${ref_getChildBucketAndIndex}(
750
+ childOutputPlan,
751
+ this,
752
+ bucket,
753
+ bucketIndex,
754
+ i,
755
+ );
756
+ if (c !== null) {
757
+ ([childBucket, childBucketIndex] = c);
758
+ } else {
759
+ childBucket = childBucketIndex = null;
760
+ }
761
+ }
762
+
763
+ mutablePath[mutablePathIndex] = i;
764
+ ${asString ? te_ifIGt0StrPlusEqualComma : tamedevil_1.default.blank}
765
+ ${makeExecuteChildPlanCode(asString ? te_stringPlusEquals : te_dataIEquals, te_thisDotLocationDetails, te_childOutputPlan, childIsNonNull, asString)}
766
+ }
767
+
768
+ mutablePath.length = mutablePathIndex;
769
+ ${asString ? te_stringPlusEqualsRbracket : tamedevil_1.default.blank}
770
+ }
771
+ ${canStream
772
+ ? (0, tamedevil_1.default) `\
773
+ const stream = bucketRootValue[${ref_$$streamMore}] /* as | AsyncIterableIterator<any> | undefined*/;
774
+ if (stream !== undefined) {
775
+ root.streams.push({
776
+ root,
777
+ path: mutablePath.slice(1),
778
+ bucket,
779
+ bucketIndex,
780
+ outputPlan: childOutputPlan,
781
+ label: childOutputPlan.layerPlan.reason.stream?.label,
782
+ stream,
783
+ startIndex: bucketRootValue.length,
784
+ });
785
+ }`
786
+ : tamedevil_1.default.blank}
787
+
788
+ return ${asString ? te_string : te_data};
789
+ `,
790
+ nameExtra: (0, tamedevil_1.default) `array${childIsNonNull ? te_underscoreNonNull : tamedevil_1.default.blank}${canStream ? te_underscoreStream : tamedevil_1.default.blank}`,
791
+ asString,
792
+ });
793
+ }
794
+ const arrayExecutor_nullable = makeArrayExecutor(false, false, false);
795
+ const arrayExecutor_nullable_streaming = makeArrayExecutor(false, true, false);
796
+ const arrayExecutor_nonNullable = makeArrayExecutor(true, false, false);
797
+ const arrayExecutor_nonNullable_streaming = makeArrayExecutor(true, true, false);
798
+ const arrayExecutorString_nullable = makeArrayExecutor(false, false, true);
799
+ const arrayExecutorString_nullable_streaming = makeArrayExecutor(false, true, true);
800
+ const arrayExecutorString_nonNullable = makeArrayExecutor(true, false, true);
801
+ const arrayExecutorString_nonNullable_streaming = makeArrayExecutor(true, true, true);
802
+ /**
803
+ * This piggy-backs off of GraphQL.js by rewriting the request, executing it in
804
+ * GraphQL.js, and then patching it into our response. It's a temporary
805
+ * workaround until we can afford the time to write our own introspection
806
+ * execution.
807
+ *
808
+ * ENHANCE: write our own introspection execution!
809
+ */
810
+ const introspect = (root, outputPlan, mutablePath, asString) => {
811
+ const { locationDetails } = outputPlan;
812
+ const { field: rawField, introspectionCacheByVariableValues, variableNames, } = outputPlan.type;
813
+ const field = {
814
+ ...rawField,
815
+ alias: { kind: Kind.NAME, value: "a" },
816
+ };
817
+ const document = {
818
+ definitions: [
819
+ {
820
+ kind: Kind.OPERATION_DEFINITION,
821
+ operation: OperationTypeNode.QUERY,
822
+ selectionSet: {
823
+ kind: Kind.SELECTION_SET,
824
+ selections: [field],
825
+ },
826
+ variableDefinitions: outputPlan.layerPlan.operationPlan.operation.variableDefinitions?.filter((d) => variableNames.includes(d.variable.name.value)),
827
+ },
828
+ ...Object.values(outputPlan.layerPlan.operationPlan.fragments),
829
+ ],
830
+ kind: Kind.DOCUMENT,
831
+ };
832
+ const variableValues = Object.create(null);
833
+ const sortedVariableNames = [...variableNames].sort();
834
+ for (const variableName of sortedVariableNames) {
835
+ variableValues[variableName] = root.variables[variableName];
836
+ }
837
+ // "canonical" only to one level, but given introspection doesn't really
838
+ // accept objects this should be mostly sufficient for decent optimization
839
+ // level.
840
+ const canonical = JSON.stringify(variableValues);
841
+ const cached = introspectionCacheByVariableValues.get(canonical);
842
+ if (cached !== undefined) {
843
+ return asString ? JSON.stringify(cached) : cached;
844
+ }
845
+ const graphqlResult = executeSync({
846
+ schema: outputPlan.layerPlan.operationPlan.schema,
847
+ document,
848
+ variableValues,
849
+ });
850
+ if (graphqlResult.errors !== undefined) {
851
+ // NOTE: we should map the introspection path, however that might require
852
+ // us to be able to raise multiple errors. Theoretically if the query
853
+ // validates we shouldn't be able to get errors out of the introspection
854
+ // system, so we'll skip over this for now. When we get around to
855
+ // implementing introspection natively in Grafast rather than piggy-backing
856
+ // off of GraphQL.js then this problem will go away on its own.
857
+ console.error("INTROSPECTION FAILED!");
858
+ console.error(graphqlResult);
859
+ const { node } = locationDetails;
860
+ throw new GraphQLError("INTROSPECTION FAILED!", node, null, null, mutablePath.slice(1), null, null);
861
+ }
862
+ const result = graphqlResult.data.a;
863
+ introspectionCacheByVariableValues.set(canonical, result);
864
+ return asString ? JSON.stringify(result) : result;
865
+ };
866
+ const ref_introspect = tamedevil_1.default.ref(introspect, "introspect");
867
+ const introspectionExecutor = makeExecutor({
868
+ inner: (0, tamedevil_1.default) ` return ${ref_introspect}(root, this, mutablePath, false)`,
869
+ nameExtra: tamedevil_1.default.cache `introspection`,
870
+ asString: false,
871
+ skipNullHandling: true,
872
+ });
873
+ const introspectionExecutorString = makeExecutor({
874
+ inner: (0, tamedevil_1.default) ` return ${ref_introspect}(root, this, mutablePath, true)`,
875
+ nameExtra: tamedevil_1.default.cache `introspection`,
876
+ asString: true,
877
+ skipNullHandling: true,
878
+ });
879
+ const SAFE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
880
+ function makeObjectExecutor(typeName, fieldTypes, hasDeferredOutputPlans,
881
+ // this.type.mode === "root",
882
+ isRoot, asString, callback) {
883
+ if (!SAFE_NAME.test(typeName)) {
884
+ throw new Error(`Unsafe type name: ${typeName}; doesn't conform to 'Name' in the GraphQL spec`);
885
+ }
886
+ const keys = [];
887
+ const fieldSpecs = [];
888
+ let signature = (asString ? "s" : "o") +
889
+ (isRoot ? "r" : "") +
890
+ (hasDeferredOutputPlans ? "d" : "");
891
+ let hasChildBucketReference = false;
892
+ for (const [key, fieldSpec] of Object.entries(fieldTypes)) {
893
+ if (!SAFE_NAME.test(key)) {
894
+ // This should not be able to happen if the GraphQL operation is valid
895
+ throw new Error(`Unsafe key: ${key}; doesn't conform to 'Name' in the GraphQL spec`);
896
+ }
897
+ keys.push(key);
898
+ fieldSpecs.push(fieldSpec);
899
+ const { fieldType, sameBucket } = fieldSpec;
900
+ switch (fieldType) {
901
+ case "__typename": {
902
+ signature += "_";
903
+ break;
904
+ }
905
+ case "outputPlan?": {
906
+ signature += "?";
907
+ break;
908
+ }
909
+ case "outputPlan!": {
910
+ signature += "!";
911
+ break;
912
+ }
913
+ default: {
914
+ const never = fieldType;
915
+ throw new Error(`Unsupported field type '${never}'`);
916
+ }
917
+ }
918
+ if (!sameBucket) {
919
+ hasChildBucketReference = true;
920
+ signature += "~";
921
+ }
922
+ }
923
+ withObjectExecutorFactory(signature, fieldSpecs, hasDeferredOutputPlans, isRoot, asString, hasChildBucketReference, (factory) => {
924
+ const fn = factory(typeName, ...keys);
925
+ callback(fn);
926
+ });
927
+ }
928
+ const makeObjectExecutorCache = new lru_1.default({
929
+ maxLength: 1000,
930
+ });
931
+ const makingObjectExecutorCallbacks = new Map();
932
+ function withObjectExecutorFactory(signature, fieldSpecs, hasDeferredOutputPlans, isRoot, asString, hasChildBucketReference, callback) {
933
+ const fn = makeObjectExecutorCache.get(signature);
934
+ if (fn !== undefined) {
935
+ return callback(fn);
936
+ }
937
+ const building = makingObjectExecutorCallbacks.get(signature);
938
+ if (building !== undefined) {
939
+ building.push(callback);
940
+ return;
941
+ }
942
+ const callbacks = [callback];
943
+ makingObjectExecutorCallbacks.set(signature, callbacks);
944
+ const inner = (0, tamedevil_1.default) `\
945
+ ${asString ? te_letStringLbrace : te_constObjEqualsObjectCreateNull}
946
+ const { keys } = this;
947
+ const mutablePathIndex = mutablePath.push("!") - 1;
948
+ let spec${hasChildBucketReference ? te_childBucketCBIDC : tamedevil_1.default.blank}, fieldResult;
949
+
950
+ ${tamedevil_1.default.join(fieldSpecs.map(({ fieldType, sameBucket }, i) => {
951
+ const te_fieldNameI = tamedevil_1.default.identifier(`fieldName_${i}`);
952
+ switch (fieldType) {
953
+ case "__typename": {
954
+ if (asString) {
955
+ // NOTE: this code relies on the fact that fieldName and typeName do
956
+ // not require any quoting in JSON/JS - they must conform to GraphQL
957
+ // `Name`.
958
+ return (0, tamedevil_1.default) ` string += \`${i === 0 ? tamedevil_1.default.blank : te_comma}"\${${te_fieldNameI}}":"\${typeName}"\`;\n`;
959
+ }
960
+ else {
961
+ return (0, tamedevil_1.default) ` obj[${te_fieldNameI}] = typeName;\n`;
962
+ }
963
+ }
964
+ case "outputPlan!":
965
+ case "outputPlan?": {
966
+ return (0, tamedevil_1.default) `\
967
+ mutablePath[mutablePathIndex] = ${te_fieldNameI};
968
+ spec = keys[${te_fieldNameI}];
969
+ ${asString
970
+ ? (0, tamedevil_1.default) ` string += \`${i === 0 ? tamedevil_1.default.blank : te_comma}"\${${te_fieldNameI}}":\`;
971
+ `
972
+ : tamedevil_1.default.blank}\
973
+ ${sameBucket
974
+ ? (0, tamedevil_1.default) `\
975
+ ${makeExecuteChildPlanCode(asString ? te_stringPlusEquals : (0, tamedevil_1.default) `obj[${te_fieldNameI}] =`, te_specDotLocationDetails, te_specDotOutputPlan, fieldType === "outputPlan!", asString, te_bucket, te_bucketIndex)}`
976
+ : (0, tamedevil_1.default) `\
977
+ directChild = bucket.children[spec.outputPlan.layerPlanId];
978
+ if (directChild !== undefined) {
979
+ childBucket = directChild.bucket;
980
+ childBucketIndex = directChild.map.get(bucketIndex);
981
+ } else {
982
+ const c = ${ref_getChildBucketAndIndex}(
983
+ spec.outputPlan,
984
+ this,
985
+ bucket,
986
+ bucketIndex,
987
+ );
988
+ if (c !== null) {
989
+ ([childBucket, childBucketIndex] = c);
990
+ } else {
991
+ childBucket = childBucketIndex = null;
992
+ }
993
+ }
994
+ ${makeExecuteChildPlanCode(asString ? te_stringPlusEquals : (0, tamedevil_1.default) `obj[${te_fieldNameI}] =`, te_specDotLocationDetails, te_specDotOutputPlan, fieldType === "outputPlan!", asString)}`}
995
+ `;
996
+ }
997
+ default: {
998
+ const never = fieldType;
999
+ throw new Error(`GrafastInternalError<879082f4-fe6f-4112-814f-852b9932ca83>: unsupported key type ${never}`);
1000
+ }
1001
+ }
1002
+ }), "\n")}
1003
+
1004
+ mutablePath.length = mutablePathIndex;
1005
+ ${asString ? te_stringPlusEqualsRbrace : tamedevil_1.default.blank}\
1006
+ ${hasDeferredOutputPlans ? te_handleDeferred : tamedevil_1.default.blank}
1007
+ return ${asString ? te_string : te_obj};`;
1008
+ const executorExpression = makeExecutorExpression({
1009
+ inner: inner,
1010
+ nameExtra: te_object,
1011
+ asString,
1012
+ skipNullHandling: isRoot,
1013
+ preamble: tamedevil_1.default.blank,
1014
+ });
1015
+ const factoryExpression = (0, tamedevil_1.default) `\
1016
+ function objectExecutorFactory(typeName${tamedevil_1.default.join(fieldSpecs.map((_, i) => (0, tamedevil_1.default) `, ${tamedevil_1.default.identifier(`fieldName_${i}`)}`), "")}) {
1017
+ return ${executorExpression};
1018
+ }`;
1019
+ tamedevil_1.default.runInBatch(factoryExpression, (factory) => {
1020
+ makeObjectExecutorCache.set(signature, factory);
1021
+ makingObjectExecutorCallbacks.delete(signature);
1022
+ for (const callback of callbacks) {
1023
+ callback(factory);
1024
+ }
1025
+ });
1026
+ }
1027
+ const makeCache = new lru_1.default({
1028
+ maxLength: 1000,
1029
+ });
1030
+ const makingCache = new Map();
1031
+ function withFastExpression(path, fallback, callback) {
1032
+ const signature = (fallback === undefined ? "d" : "f") + "_" + path.join("|");
1033
+ const existing = makeCache.get(signature);
1034
+ if (existing !== undefined) {
1035
+ callback(existing);
1036
+ return;
1037
+ }
1038
+ const existingCallbacks = makingCache.get(signature);
1039
+ if (existingCallbacks !== undefined) {
1040
+ existingCallbacks.push(callback);
1041
+ return;
1042
+ }
1043
+ const callbacks = [callback];
1044
+ makingCache.set(signature, callbacks);
1045
+ const jitParts = [];
1046
+ for (let i = 0, l = path.length; i < l; i++) {
1047
+ const part = path[i];
1048
+ jitParts.push(tamedevil_1.default.optionalGet(part));
1049
+ }
1050
+ const expression = tamedevil_1.default.join(jitParts, "");
1051
+ tamedevil_1.default.runInBatch((0, tamedevil_1.default) `(value => (value${expression})${fallback !== undefined ? (0, tamedevil_1.default) ` ?? ${tamedevil_1.default.lit(fallback)}` : tamedevil_1.default.blank})`, (fn) => {
1052
+ makeCache.set(signature, fn);
1053
+ makingCache.delete(signature);
1054
+ for (const callback of callbacks) {
1055
+ callback(fn);
1056
+ }
1057
+ });
1058
+ }
1059
+ //# sourceMappingURL=OutputPlan.js.map