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,2543 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OperationPlan = exports.POLYMORPHIC_ROOT_PATHS = exports.POLYMORPHIC_ROOT_PATH = 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.__importDefault(require("tamedevil"));
8
+ const assert = tslib_1.__importStar(require("../assert.js"));
9
+ const graphqlCollectFields_js_1 = require("../graphqlCollectFields.js");
10
+ const graphqlMergeSelectionSets_js_1 = require("../graphqlMergeSelectionSets.js");
11
+ const index_js_1 = require("../index.js");
12
+ const input_js_1 = require("../input.js");
13
+ const inspect_js_1 = require("../inspect.js");
14
+ const interfaces_js_1 = require("../interfaces.js");
15
+ const operationPlan_input_js_1 = require("../operationPlan-input.js");
16
+ const step_js_1 = require("../step.js");
17
+ const access_js_1 = require("../steps/access.js");
18
+ const constant_js_1 = require("../steps/constant.js");
19
+ const graphqlResolver_js_1 = require("../steps/graphqlResolver.js");
20
+ const timeSource_js_1 = require("../timeSource.js");
21
+ const utils_js_1 = require("../utils.js");
22
+ const LayerPlan_js_1 = require("./LayerPlan.js");
23
+ const withGlobalLayerPlan_js_1 = require("./lib/withGlobalLayerPlan.js");
24
+ const OutputPlan_js_1 = require("./OutputPlan.js");
25
+ const StepTracker_js_1 = require("./StepTracker.js");
26
+ const atpe = typeof process !== "undefined" && process.env.ALWAYS_THROW_PLANNING_ERRORS;
27
+ const ALWAYS_THROW_PLANNING_ERRORS = atpe === "1";
28
+ const THROW_PLANNING_ERRORS_ON_SIDE_EFFECTS = atpe === "2";
29
+ /**
30
+ * Returns true for steps that the system populates automatically without executing.
31
+ */
32
+ function isPrepopulatedStep(step) {
33
+ return step instanceof index_js_1.__ItemStep || step instanceof index_js_1.__ValueStep;
34
+ }
35
+ if (atpe &&
36
+ atpe !== "0" &&
37
+ !(ALWAYS_THROW_PLANNING_ERRORS || THROW_PLANNING_ERRORS_ON_SIDE_EFFECTS)) {
38
+ throw new Error(`Invalid value for envvar 'ALWAYS_THROW_PLANNING_ERRORS' - expected '1' or '2', but received '${atpe}'`);
39
+ }
40
+ // Work around TypeScript CommonJS `graphql_1.isListType` unoptimal access.
41
+ const { assertObjectType, defaultFieldResolver, getNamedType, getNullableType, isEnumType, isInterfaceType, isListType, isNonNullType, isObjectType, isScalarType, isUnionType, } = graphql;
42
+ /**
43
+ * This value allows the first few plans to take more time whilst the JIT warms
44
+ * up - planning can easily go from 400ms down to 80ms after just a few
45
+ * executions thanks to V8's JIT.
46
+ */
47
+ let planningTimeoutWarmupMultiplier = 5;
48
+ const EMPTY_ARRAY = Object.freeze([]);
49
+ exports.POLYMORPHIC_ROOT_PATH = null;
50
+ exports.POLYMORPHIC_ROOT_PATHS = null;
51
+ Object.freeze(exports.POLYMORPHIC_ROOT_PATHS);
52
+ /** In development we might run additional checks */
53
+ const isDev = typeof process !== "undefined" &&
54
+ (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test");
55
+ /** How many times will we try re-optimizing before giving up */
56
+ const MAX_OPTIMIZATION_LOOPS = 10;
57
+ const REASON_ROOT = Object.freeze({ type: "root" });
58
+ const OUTPUT_PLAN_TYPE_NULL = Object.freeze({ mode: "null" });
59
+ const OUTPUT_PLAN_TYPE_ARRAY = Object.freeze({ mode: "array" });
60
+ const newValueStepCallback = () => new index_js_1.__ValueStep();
61
+ const NO_ARGS = {
62
+ get() {
63
+ throw new Error(`This field doesn't have any arguments`);
64
+ },
65
+ };
66
+ class OperationPlan {
67
+ constructor(schema, operation, fragments, variableValues, context, rootValue, planningTimeout) {
68
+ this.schema = schema;
69
+ this.operation = operation;
70
+ this.fragments = fragments;
71
+ this.variableValues = variableValues;
72
+ this.context = context;
73
+ this.rootValue = rootValue;
74
+ this.planningTimeout = planningTimeout;
75
+ /**
76
+ * What state is the OpPlan in?
77
+ *
78
+ * 1. init
79
+ * 2. plan
80
+ * 3. validate
81
+ * 5. optimize
82
+ * 6. finalize
83
+ * 7. ready
84
+ *
85
+ * Once in 'ready' state we can execute the plan.
86
+ *
87
+ * @internal
88
+ */
89
+ this.phase = "init";
90
+ /**
91
+ * Gets updated as we work our way through the plan, useful for making errors more helpful.
92
+ */
93
+ this.loc = isDev
94
+ ? []
95
+ : null /* forbid loc in production */;
96
+ this.modifierStepCount = 0;
97
+ this.modifierDepthCount = 0;
98
+ this.modifierSteps = [];
99
+ /** @internal */
100
+ this.stepTracker = new StepTracker_js_1.StepTracker(this);
101
+ this.maxDeduplicatedStepId = -1;
102
+ this.maxValidatedStepId = -1;
103
+ /** Constraints based on evaluating variables. @internal */
104
+ this.variableValuesConstraints = [];
105
+ /** Constraints based on evaluating context. @internal */
106
+ this.contextConstraints = [];
107
+ /** Constraints based on evaluating rootValue. @internal */
108
+ this.rootValueConstraints = [];
109
+ /**
110
+ * @internal
111
+ */
112
+ this.itemStepIdByListStepId = Object.create(null);
113
+ /**
114
+ * If true, then this operation doesn't use (custom) resolvers.
115
+ */
116
+ this.pure = true;
117
+ this.startTime = timeSource_js_1.timeSource.now();
118
+ this.previousLap = this.startTime;
119
+ this.laps = [];
120
+ this.optimizeMeta = new Map();
121
+ /** @internal Use plan.getStep(id) instead. */
122
+ this.getStep = isDev
123
+ ? (id, requestingStep) => {
124
+ if (!["plan", "validate", "optimize"].includes(this.phase)) {
125
+ throw new Error(`Getting a step during the '${this.phase}' phase is forbidden - please do so before or during the optimize phase.`);
126
+ }
127
+ // Check that requestingStep is allowed to get steps
128
+ if (requestingStep.isOptimized &&
129
+ (this.phase !== "optimize" ||
130
+ !requestingStep.allowMultipleOptimizations)) {
131
+ throw new Error(`Optimized step ${requestingStep} is not permitted to request other steps (requested '${id}')`);
132
+ }
133
+ const step = this.stepTracker.getStepById(id);
134
+ if (step == null) {
135
+ throw new Error(`Programming error: step with id '${id}' no longer exists (attempted access from ${requestingStep}). Most likely this means that ${requestingStep} has an illegal reference to this step, you should only maintain references to steps via dependencies.`);
136
+ }
137
+ return step[interfaces_js_1.$$proxy] ?? step;
138
+ }
139
+ : (id, _requestingStep) => {
140
+ const step = this.stepTracker.getStepById(id);
141
+ return step[interfaces_js_1.$$proxy] ?? step;
142
+ };
143
+ this.polymorphicLayerPlanByPathByLayerPlan = new Map();
144
+ this.scalarPlanInfo = { schema: this.schema };
145
+ const queryType = schema.getQueryType();
146
+ assert.ok(queryType, "Schema must have a query type");
147
+ this.queryType = queryType;
148
+ this.mutationType = schema.getMutationType() ?? null;
149
+ this.subscriptionType = schema.getSubscriptionType() ?? null;
150
+ const allTypes = Object.values(schema.getTypeMap());
151
+ const allUnions = [];
152
+ const allObjectTypes = [];
153
+ this.unionsContainingObjectType = Object.create(null);
154
+ for (const type of allTypes) {
155
+ if (isUnionType(type)) {
156
+ allUnions.push(type);
157
+ const members = type.getTypes();
158
+ for (const memberType of members) {
159
+ if (!this.unionsContainingObjectType[memberType.name]) {
160
+ this.unionsContainingObjectType[memberType.name] = [type];
161
+ }
162
+ else {
163
+ this.unionsContainingObjectType[memberType.name].push(type);
164
+ }
165
+ }
166
+ }
167
+ else if (isObjectType(type)) {
168
+ allObjectTypes.push(type);
169
+ if (!this.unionsContainingObjectType[type.name]) {
170
+ this.unionsContainingObjectType[type.name] = [];
171
+ }
172
+ }
173
+ }
174
+ this.operationType = operation.operation;
175
+ this.phase = "plan";
176
+ this.rootLayerPlan = new LayerPlan_js_1.LayerPlan(this, null, REASON_ROOT);
177
+ // This doesn't do anything, it only exists to align plans more closely
178
+ // with an older version of the planner (to reduce the diff).
179
+ const rootSelectionSetStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, newValueStepCallback);
180
+ this.rootLayerPlan.setRootStep(rootSelectionSetStep);
181
+ // Set up the shared steps for variables, context and rootValue
182
+ [this.variableValuesStep, this.trackedVariableValuesStep] = this.track(variableValues, this.variableValuesConstraints, this.operation.variableDefinitions);
183
+ [this.contextStep, this.trackedContextStep] = this.track(context, this.contextConstraints);
184
+ [this.rootValueStep, this.trackedRootValueStep] = this.track(rootValue, this.rootValueConstraints);
185
+ // this.rootLayerPlan.parentStep = this.trackedRootValueStep;
186
+ this.deduplicateSteps();
187
+ this.lap("init");
188
+ // Plan the operation
189
+ this.planOperation();
190
+ this.checkTimeout();
191
+ this.lap("planOperation");
192
+ // Now perform hoisting (and repeat deduplication)
193
+ this.hoistSteps();
194
+ this.checkTimeout();
195
+ this.lap("hoistSteps", "planOperation");
196
+ if (isDev) {
197
+ this.phase = "validate";
198
+ // Helpfully check steps don't do forbidden things.
199
+ this.validateSteps();
200
+ this.lap("validateSteps");
201
+ }
202
+ this.phase = "optimize";
203
+ // Get rid of temporary steps before `optimize` triggers side-effects.
204
+ // (Critical due to steps that may have been discarded due to field errors
205
+ // or similar.)
206
+ this.stepTracker.treeShakeSteps();
207
+ this.checkTimeout();
208
+ this.lap("treeShakeSteps", "optimize");
209
+ // Replace/inline/optimise steps
210
+ tamedevil_1.default.batch(() => {
211
+ this.optimizeSteps();
212
+ });
213
+ this.checkTimeout();
214
+ this.lap("optimizeSteps");
215
+ this.phase = "finalize";
216
+ this.stepTracker.finalizeSteps();
217
+ // Get rid of steps that are no longer needed after optimising outputPlans
218
+ // (we shouldn't see any new steps or dependencies after here)
219
+ this.stepTracker.treeShakeSteps();
220
+ this.checkTimeout();
221
+ this.lap("treeShakeSteps", "finalize");
222
+ // Now shove steps as deep down as they can go (opposite of hoist)
223
+ this.pushDownSteps();
224
+ this.checkTimeout();
225
+ this.lap("pushDownSteps");
226
+ // Plans are expected to execute later; they may take steps here to prepare
227
+ // themselves (e.g. compiling SQL queries ahead of time).
228
+ tamedevil_1.default.batch(() => {
229
+ this.finalizeSteps();
230
+ });
231
+ this.lap("finalizeSteps");
232
+ // Replace access plans with direct access, etc (must come after finalizeSteps)
233
+ tamedevil_1.default.batch(() => {
234
+ this.optimizeOutputPlans();
235
+ });
236
+ this.checkTimeout();
237
+ this.lap("optimizeOutputPlans");
238
+ // AccessSteps may have been removed
239
+ this.stepTracker.treeShakeSteps();
240
+ this.checkTimeout();
241
+ this.lap("treeShakeSteps", "optimizeOutputPlans");
242
+ this.stepTracker.finalizeOutputPlans();
243
+ tamedevil_1.default.batch(() => {
244
+ this.finalizeLayerPlans();
245
+ });
246
+ this.lap("finalizeLayerPlans");
247
+ tamedevil_1.default.batch(() => {
248
+ this.finalizeOutputPlans();
249
+ });
250
+ this.lap("finalizeOutputPlans");
251
+ this.phase = "ready";
252
+ // this.walkFinalizedPlans();
253
+ // this.preparePrefetches();
254
+ const allMetaKeys = new Set();
255
+ for (const step of this.stepTracker.activeSteps) {
256
+ if (step.metaKey !== undefined) {
257
+ allMetaKeys.add(step.metaKey);
258
+ }
259
+ }
260
+ const allMetaKeysList = [...allMetaKeys];
261
+ this.makeMetaByMetaKey = makeMetaByMetaKeysFactory(allMetaKeysList);
262
+ this.lap("ready");
263
+ const elapsed = timeSource_js_1.timeSource.now() - this.startTime;
264
+ /*
265
+ console.log(`Planning took ${elapsed.toFixed(1)}ms`);
266
+ const entries: Array<{ process: string; duration: string }> = [];
267
+ for (const lap of this.laps) {
268
+ const elapsed = lap.elapsed;
269
+ entries.push({
270
+ process: `${lap.category}${
271
+ lap.subcategory ? `[${lap.subcategory}]` : ``
272
+ }`,
273
+ duration: `${elapsed.toFixed(1)}ms`,
274
+ });
275
+ }
276
+ console.table(entries);
277
+ */
278
+ // Allow this to be garbage collected
279
+ this.optimizeMeta = null;
280
+ context?.grafastMetricsEmitter?.emit("plan", {
281
+ elapsed,
282
+ laps: this.laps,
283
+ });
284
+ if (planningTimeoutWarmupMultiplier > 1) {
285
+ planningTimeoutWarmupMultiplier = Math.max(1, planningTimeoutWarmupMultiplier - 0.5);
286
+ }
287
+ }
288
+ lap(category, subcategory) {
289
+ const now = timeSource_js_1.timeSource.now();
290
+ const elapsed = now - this.previousLap;
291
+ this.previousLap = now;
292
+ this.laps.push({ category, subcategory, elapsed });
293
+ }
294
+ checkTimeout() {
295
+ if (this.planningTimeout === null)
296
+ return;
297
+ const now = timeSource_js_1.timeSource.now();
298
+ const elapsed = now - this.startTime;
299
+ if (elapsed > this.planningTimeout * planningTimeoutWarmupMultiplier) {
300
+ throw new index_js_1.SafeError("Operation took too long to plan and was aborted. Please simplify the request and try again.", {
301
+ [interfaces_js_1.$$timeout]: this.planningTimeout,
302
+ [interfaces_js_1.$$ts]: now,
303
+ });
304
+ }
305
+ }
306
+ /**
307
+ * Called by the LayerPlan's constructor when it wants to get a new id to use.
308
+ *
309
+ * @internal
310
+ */
311
+ addLayerPlan(layerPlan) {
312
+ return this.stepTracker.addLayerPlan(layerPlan);
313
+ }
314
+ /**
315
+ * Adds a plan to the known steps and returns the number to use as the plan
316
+ * id. ONLY to be used from Step, user code should never call this directly.
317
+ *
318
+ * @internal
319
+ */
320
+ _addStep(plan) {
321
+ if (!["plan", "validate", "optimize"].includes(this.phase)) {
322
+ throw new Error(`Creating a plan during the '${this.phase}' phase is forbidden.`);
323
+ }
324
+ return this.stepTracker.addStep(plan);
325
+ }
326
+ /**
327
+ * Adds a plan to the known steps and returns the number to use as the plan
328
+ * id. ONLY to be used from Step, user code should never call this directly.
329
+ *
330
+ * @internal
331
+ */
332
+ _addModifierStep(step) {
333
+ if (!["plan", "validate", "optimize"].includes(this.phase)) {
334
+ throw new Error(`Creating a step during the '${this.phase}' phase is forbidden.`);
335
+ }
336
+ const modifierStepId = `${this.modifierDepthCount}-${this
337
+ .modifierStepCount++}`;
338
+ this.modifierSteps.push(step);
339
+ return modifierStepId;
340
+ }
341
+ /**
342
+ * Get a plan without specifying who requested it; this disables all the
343
+ * caller checks. Only intended to be called from internal code.
344
+ *
345
+ * @internal
346
+ */
347
+ dangerouslyGetStep(id) {
348
+ return this.stepTracker.getStepById(id);
349
+ }
350
+ planOperation() {
351
+ try {
352
+ switch (this.operationType) {
353
+ case "query": {
354
+ this.planQuery();
355
+ break;
356
+ }
357
+ case "mutation": {
358
+ this.planMutation();
359
+ break;
360
+ }
361
+ case "subscription": {
362
+ this.planSubscription();
363
+ break;
364
+ }
365
+ default: {
366
+ const never = this.operationType;
367
+ throw new index_js_1.SafeError(`Unsupported operation type '${never}'.`);
368
+ }
369
+ }
370
+ }
371
+ catch (e) {
372
+ // LOGGING: raise this somewhere critical
373
+ if (this.loc != null) {
374
+ console.error(`Error occurred during query planning (at ${this.loc.join(" > ")}): \n${e.stack || e}`);
375
+ }
376
+ else {
377
+ console.error(`Error occurred during query planning: \n${e.stack || e}`);
378
+ }
379
+ throw new Error(`Query planning error: ${e.message}`, { cause: e });
380
+ }
381
+ }
382
+ /**
383
+ * Plans a GraphQL query operation.
384
+ */
385
+ planQuery() {
386
+ if (this.loc !== null)
387
+ this.loc.push("planQuery()");
388
+ const rootType = this.queryType;
389
+ if (!rootType) {
390
+ throw new index_js_1.SafeError("No query type found in schema");
391
+ }
392
+ const locationDetails = {
393
+ node: this.operation.selectionSet.selections,
394
+ parentTypeName: null,
395
+ // WHAT SHOULD fieldName be here?!
396
+ fieldName: null,
397
+ };
398
+ const outputPlan = new OutputPlan_js_1.OutputPlan(this.rootLayerPlan, this.rootValueStep, {
399
+ mode: "root",
400
+ typeName: this.queryType.name,
401
+ }, locationDetails);
402
+ this.rootOutputPlan = outputPlan;
403
+ this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, this.trackedRootValueStep, rootType, this.operation.selectionSet.selections);
404
+ if (this.loc !== null)
405
+ this.loc.pop();
406
+ }
407
+ /**
408
+ * Implements the `PlanOpPlanMutation` algorithm.
409
+ */
410
+ planMutation() {
411
+ if (this.loc !== null)
412
+ this.loc.push("planMutation()");
413
+ const rootType = this.mutationType;
414
+ if (!rootType) {
415
+ throw new index_js_1.SafeError("No mutation type found in schema");
416
+ }
417
+ this.deduplicateSteps();
418
+ const locationDetails = {
419
+ node: this.operation.selectionSet.selections,
420
+ parentTypeName: null,
421
+ // WHAT SHOULD fieldName be here?!
422
+ fieldName: null,
423
+ };
424
+ const outputPlan = new OutputPlan_js_1.OutputPlan(this.rootLayerPlan, this.rootValueStep, {
425
+ mode: "root",
426
+ typeName: rootType.name,
427
+ }, locationDetails);
428
+ this.rootOutputPlan = outputPlan;
429
+ this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, this.trackedRootValueStep, rootType, this.operation.selectionSet.selections, true);
430
+ if (this.loc !== null)
431
+ this.loc.pop();
432
+ }
433
+ /**
434
+ * Implements the `PlanOpPlanSubscription` algorithm.
435
+ */
436
+ planSubscription() {
437
+ if (this.loc !== null)
438
+ this.loc.push("planSubscription");
439
+ const rootType = this.subscriptionType;
440
+ if (!rootType) {
441
+ throw new index_js_1.SafeError("No subscription type found in schema");
442
+ }
443
+ const selectionSet = this.operation.selectionSet;
444
+ const groupedFieldSet = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, graphqlCollectFields_js_1.graphqlCollectFields, null, this, this.trackedRootValueStep.id, rootType, selectionSet.selections);
445
+ if (groupedFieldSet.deferred !== undefined) {
446
+ throw new index_js_1.SafeError("@defer forbidden on subscription root selection set");
447
+ }
448
+ let firstKey = undefined;
449
+ for (const key of groupedFieldSet.fields.keys()) {
450
+ if (firstKey !== undefined) {
451
+ throw new index_js_1.SafeError("subscriptions may only have one top-level field");
452
+ }
453
+ firstKey = key;
454
+ }
455
+ assert.ok(firstKey != null, "selection set cannot be empty");
456
+ const fields = groupedFieldSet.fields.get(firstKey);
457
+ if (!fields) {
458
+ throw new index_js_1.SafeError("Consistency error.");
459
+ }
460
+ // All grouped fields are equivalent, as mandated by GraphQL validation rules. Thus we can take the first one.
461
+ const field = fields[0];
462
+ const fieldName = field.name.value; // Unaffected by alias.
463
+ const rootTypeFields = rootType.getFields();
464
+ const fieldSpec = rootTypeFields[fieldName];
465
+ const rawSubscriptionPlanResolver = fieldSpec.extensions?.grafast?.subscribePlan;
466
+ const path = [field.alias?.value ?? fieldName];
467
+ const locationDetails = {
468
+ parentTypeName: rootType.name,
469
+ fieldName,
470
+ node: this.operation.selectionSet.selections,
471
+ };
472
+ const subscriptionPlanResolver = rawSubscriptionPlanResolver;
473
+ const fieldArgsSpec = fieldSpec.args;
474
+ const trackedArguments = fieldArgsSpec.length > 0
475
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, this.getTrackedArguments, this, fieldArgsSpec, field)
476
+ : NO_ARGS;
477
+ if (subscriptionPlanResolver !== undefined) {
478
+ // PERF: optimize this
479
+ const { haltTree, step: subscribeStep } = this.planField(this.rootLayerPlan, path, exports.POLYMORPHIC_ROOT_PATHS, fields, subscriptionPlanResolver, "autoApplyAfterParentSubscribePlan", this.trackedRootValueStep, fieldSpec, trackedArguments);
480
+ if (haltTree) {
481
+ throw new index_js_1.SafeError("Failed to setup subscription");
482
+ }
483
+ const stepOptions = {
484
+ stream: { initialCount: 0 },
485
+ };
486
+ subscribeStep._stepOptions = stepOptions;
487
+ this.rootLayerPlan.setRootStep(subscribeStep);
488
+ const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, this.rootLayerPlan, {
489
+ type: "subscription",
490
+ });
491
+ const $__item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => new index_js_1.__ItemStep(subscribeStep));
492
+ subscriptionEventLayerPlan.setRootStep($__item);
493
+ let streamItemPlan = (0, utils_js_1.hasItemPlan)(subscribeStep)
494
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, subscribeStep.itemPlan, subscribeStep, $__item)
495
+ : $__item;
496
+ // WE MUST RE-FETCH STEPS AFTER DEDUPLICATION!
497
+ this.deduplicateSteps();
498
+ streamItemPlan = this.stepTracker.getStepById(streamItemPlan.id);
499
+ const outputPlan = new OutputPlan_js_1.OutputPlan(subscriptionEventLayerPlan, this.rootValueStep, { mode: "root", typeName: rootType.name }, locationDetails);
500
+ this.rootOutputPlan = outputPlan;
501
+ this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, streamItemPlan, rootType, selectionSet.selections);
502
+ }
503
+ else {
504
+ const subscribeStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => {
505
+ const $args = (0, index_js_1.object)(field.arguments?.reduce((memo, arg) => {
506
+ memo[arg.name.value] = trackedArguments.get(arg.name.value);
507
+ return memo;
508
+ }, Object.create(null)) ?? Object.create(null));
509
+ const rawResolver = fieldSpec.resolve;
510
+ const rawSubscriber = fieldSpec.subscribe;
511
+ return (0, graphqlResolver_js_1.graphqlResolver)(rawResolver, rawSubscriber, this.trackedRootValueStep, $args, {
512
+ fieldName,
513
+ fieldNodes: fields,
514
+ fragments: this.fragments,
515
+ operation: this.operation,
516
+ parentType: this.subscriptionType,
517
+ returnType: fieldSpec.type,
518
+ schema: this.schema,
519
+ // @ts-ignore
520
+ path: {
521
+ typename: this.subscriptionType.name,
522
+ key: fieldName,
523
+ prev: undefined,
524
+ },
525
+ });
526
+ });
527
+ const stepOptions = {
528
+ stream: { initialCount: 0 },
529
+ };
530
+ subscribeStep._stepOptions = stepOptions;
531
+ this.rootLayerPlan.setRootStep(subscribeStep);
532
+ const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, this.rootLayerPlan, {
533
+ type: "subscription",
534
+ });
535
+ const $__item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => new index_js_1.__ItemStep(subscribeStep));
536
+ subscriptionEventLayerPlan.setRootStep($__item);
537
+ let streamItemPlan = (0, utils_js_1.hasItemPlan)(subscribeStep)
538
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, subscribeStep.itemPlan, subscribeStep, $__item)
539
+ : $__item;
540
+ // WE MUST RE-FETCH STEPS AFTER DEDUPLICATION!
541
+ this.deduplicateSteps();
542
+ streamItemPlan = this.stepTracker.getStepById(streamItemPlan.id);
543
+ const outputPlan = new OutputPlan_js_1.OutputPlan(subscriptionEventLayerPlan, this.rootValueStep, { mode: "root", typeName: rootType.name }, locationDetails);
544
+ this.rootOutputPlan = outputPlan;
545
+ this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, streamItemPlan, rootType, selectionSet.selections);
546
+ }
547
+ if (this.loc !== null)
548
+ this.loc.pop();
549
+ }
550
+ /**
551
+ * Gets the item plan for a given parent list plan - this ensures we only
552
+ * create one item plan per parent plan.
553
+ */
554
+ itemStepForListStep(parentLayerPlan, listStep, depth = 0) {
555
+ const itemStepId = this.itemStepIdByListStepId[listStep.id];
556
+ if (itemStepId !== undefined) {
557
+ return this.stepTracker.getStepById(itemStepId);
558
+ }
559
+ // Create a new LayerPlan for this list item
560
+ const layerPlan = new LayerPlan_js_1.LayerPlan(this, parentLayerPlan, {
561
+ type: "listItem",
562
+ parentStep: listStep,
563
+ stream: listStep._stepOptions.stream ?? undefined,
564
+ });
565
+ const itemPlan = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, listStep.polymorphicPaths, () => new index_js_1.__ItemStep(listStep, depth));
566
+ layerPlan.setRootStep(itemPlan);
567
+ this.itemStepIdByListStepId[listStep.id] = itemPlan.id;
568
+ return itemPlan;
569
+ }
570
+ processGroupedFieldSet(
571
+ // Deliberately shadows
572
+ outputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, objectTypeFields, isMutation, groupedFieldSet) {
573
+ // `__typename` shouldn't bump the mutation index since it has no side effects.
574
+ let mutationIndex = -1;
575
+ for (const [responseKey, fieldNodes] of groupedFieldSet.fields.entries()) {
576
+ // All grouped fields are equivalent, as mandated by GraphQL validation rules. Thus we can take the first one.
577
+ const field = fieldNodes[0];
578
+ const fieldName = field.name.value;
579
+ const locationDetails = {
580
+ parentTypeName: objectType.name,
581
+ fieldName,
582
+ node: fieldNodes,
583
+ };
584
+ // explicit matches are the fastest: https://jsben.ch/ajZNf
585
+ if (fieldName === "__typename") {
586
+ outputPlan.addChild(objectType, responseKey, {
587
+ type: "__typename",
588
+ locationDetails,
589
+ });
590
+ continue;
591
+ }
592
+ else if (fieldName === "__schema" || fieldName === "__type") {
593
+ const variableNames = (0, utils_js_1.findVariableNamesUsed)(this, field);
594
+ outputPlan.addChild(objectType, responseKey, {
595
+ type: "outputPlan",
596
+ isNonNull: fieldName === "__schema",
597
+ outputPlan: new OutputPlan_js_1.OutputPlan(outputPlan.layerPlan, this.rootValueStep, {
598
+ mode: "introspection",
599
+ field,
600
+ variableNames,
601
+ // PERF: if variableNames.length === 0 we should be able to optimize this!
602
+ introspectionCacheByVariableValues: new lru_1.default({
603
+ maxLength: 3,
604
+ }),
605
+ }, locationDetails),
606
+ locationDetails,
607
+ });
608
+ continue;
609
+ }
610
+ const objectField = objectTypeFields[fieldName];
611
+ if (!objectField) {
612
+ // Field does not exist; this should have been caught by validation
613
+ // but the spec says to just skip it.
614
+ continue;
615
+ }
616
+ const fieldType = objectField.type;
617
+ const rawPlanResolver = objectField.extensions?.grafast?.plan;
618
+ if (rawPlanResolver?.constructor?.name === "AsyncFunction") {
619
+ throw new Error(`Plans must be synchronous, but this schema has an async function at '${objectType.name}.${fieldName}.plan': ${rawPlanResolver.toString()}`);
620
+ }
621
+ const namedReturnType = getNamedType(fieldType);
622
+ const resolvedResolver = objectField.resolve;
623
+ const subscriber = objectField.subscribe;
624
+ const usesDefaultResolver = resolvedResolver == null || resolvedResolver === defaultFieldResolver;
625
+ const resolver = usesDefaultResolver ? null : resolvedResolver;
626
+ // Apply a default plan to fields that do not have a plan nor a resolver.
627
+ const planResolver = rawPlanResolver ??
628
+ (usesDefaultResolver ? makeDefaultPlan(fieldName) : undefined);
629
+ /*
630
+ * When considering resolvers on fields, there's three booleans to
631
+ * consider:
632
+ *
633
+ * - typeIsPlanned: Does the type the field is defined on expect a plan?
634
+ * - NOTE: the root types (Query, Mutation, Subscription) implicitly
635
+ * expect the "root plan"
636
+ * - fieldHasPlan: Does the field define a `plan()` method?
637
+ * - resultIsPlanned: Does the named type that the field returns (the
638
+ * "named field type") expect a plan?
639
+ * - NOTE: only object types, unions and interfaces may expect plans;
640
+ * but not all of them do.
641
+ * - NOTE: a union/interface expects a plan iff ANY of its object
642
+ * types expect plans
643
+ * - NOTE: if ANY object type in an interface/union expects a plan
644
+ * then ALL object types within the interface/union must expect
645
+ * plans.
646
+ * - NOTE: scalars and enums never expect a plan.
647
+ *
648
+ * These booleans impact:
649
+ *
650
+ * - Whether there must be a `plan()` declaration and what the "parent"
651
+ * argument is to the same
652
+ * - If typeIsPlanned:
653
+ * - Assert: `fieldHasPlan` must be true
654
+ * - Pass through the parent plan
655
+ * - Else, if resultIsPlanned:
656
+ * - Assert: `fieldHasPlan` must be true
657
+ * - Pass through a `__ValueStep` representing the parent value.
658
+ * - Else, if fieldHasPlan:
659
+ * - Pass through a `__ValueStep` representing the parent value.
660
+ * - Else
661
+ * - No action necessary.
662
+ * - If the field may define `resolve()` and what the "parent" argument
663
+ * is to the same
664
+ * - If resultIsPlanned
665
+ * - Assert: there must not be a `resolve()`
666
+ * - Grafast provides pure resolver.
667
+ * - Else if fieldHasPlan (which may be implied by typeIsPlanned
668
+ * above)
669
+ * - If `resolve()` is not set:
670
+ * - grafast will return the value from the plan directly
671
+ * - Otherwise:
672
+ * - Grafast will wrap this resolver and will call `resolve()` (or
673
+ * default resolver) with the plan result. IMPORTANT: you may
674
+ * want to use an `ObjectStep` so that the parent object is of
675
+ * the expected shape; e.g. your plan might return
676
+ * `object({username: $username})` for a `User.username` field.
677
+ * - Else
678
+ * - Leave `resolve()` untouched - do not even wrap it.
679
+ * - (Failing that, use a __ValueStep and return the result
680
+ * directly.)
681
+ */
682
+ if (resolver !== null) {
683
+ this.pure = false;
684
+ }
685
+ const resultIsPlanned = (0, utils_js_1.isTypePlanned)(this.schema, namedReturnType);
686
+ const fieldHasPlan = !!planResolver;
687
+ if (resultIsPlanned &&
688
+ !fieldHasPlan &&
689
+ !objectType.extensions?.grafast?.assertStep) {
690
+ throw new Error(`Field ${objectType.name}.${fieldName} returns a ${namedReturnType.name} which expects a plan to be available; however this field has no plan() method to produce such a plan; please add 'extensions.grafast.plan' to this field.`);
691
+ }
692
+ if (resultIsPlanned && resolver) {
693
+ throw new Error(`Field ${objectType.name}.${fieldName} returns a ${namedReturnType.name} which expects a plan to be available; this means that ${objectType.name}.${fieldName} is forbidden from defining a GraphQL resolver.`);
694
+ }
695
+ let step;
696
+ let haltTree = false;
697
+ const fieldLayerPlan = isMutation
698
+ ? new LayerPlan_js_1.LayerPlan(this, outputPlan.layerPlan, {
699
+ type: "mutationField",
700
+ mutationIndex: ++mutationIndex,
701
+ })
702
+ : outputPlan.layerPlan;
703
+ const objectFieldArgs = objectField.args;
704
+ const trackedArguments = objectFieldArgs.length > 0
705
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, this.getTrackedArguments, this, objectFieldArgs, field)
706
+ : NO_ARGS;
707
+ const fieldPath = [...path, responseKey];
708
+ if (typeof planResolver === "function") {
709
+ ({ step, haltTree } = this.planField(fieldLayerPlan, fieldPath, polymorphicPaths, fieldNodes, planResolver, "autoApplyAfterParentPlan", parentStep, objectField, trackedArguments));
710
+ }
711
+ else {
712
+ // No plan resolver (or plan resolver fallback) so there must be a
713
+ // `resolve` method, so we'll feed the full parent step into the
714
+ // resolver.
715
+ assert.ok(resolver !== null, "GraphileInternalError<81652257-617a-4d1a-8306-903d0e3d2ddf>: The field has no resolver, so planResolver should exist (either as the field.plan or as the default plan resolver).");
716
+ // ENHANCEMENT: should we do `step = parentStep.object()` (i.e.
717
+ // `$pgSelectSingle.record()`) or similar for "opaque" steps to become
718
+ // suitable for consumption by resolvers?
719
+ // Maybe `parentStep.forResolver()` or `parentStep.hydrate()` or `parentStep.toFullObject()`?
720
+ step = parentStep;
721
+ }
722
+ if (resolver !== null) {
723
+ step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(fieldLayerPlan, polymorphicPaths, () => {
724
+ const $args = (0, index_js_1.object)(field.arguments?.reduce((memo, arg) => {
725
+ memo[arg.name.value] = trackedArguments.get(arg.name.value);
726
+ return memo;
727
+ }, Object.create(null)) ?? Object.create(null));
728
+ return (0, graphqlResolver_js_1.graphqlResolver)(resolver, subscriber, step, $args, {
729
+ fieldName,
730
+ fieldNodes,
731
+ fragments: this.fragments,
732
+ operation: this.operation,
733
+ parentType: objectType,
734
+ returnType: fieldType,
735
+ schema: this.schema,
736
+ });
737
+ });
738
+ }
739
+ // May have changed due to deduplicate
740
+ step = this.stepTracker.getStepById(step.id);
741
+ if (haltTree) {
742
+ const isNonNull = isNonNullType(fieldType);
743
+ outputPlan.addChild(objectType, responseKey, {
744
+ type: "outputPlan",
745
+ outputPlan: new OutputPlan_js_1.OutputPlan(fieldLayerPlan, step, OUTPUT_PLAN_TYPE_NULL, locationDetails),
746
+ isNonNull,
747
+ locationDetails,
748
+ });
749
+ }
750
+ else {
751
+ this.planIntoOutputPlan(outputPlan, fieldLayerPlan, fieldPath, polymorphicPath, polymorphicPaths,
752
+ // If one field has a selection set, they all have a selection set (guaranteed by validation).
753
+ field.selectionSet != null
754
+ ? fieldNodes.flatMap((n) => n.selectionSet.selections)
755
+ : undefined, objectType, responseKey, fieldType, step, locationDetails);
756
+ }
757
+ }
758
+ if (groupedFieldSet.deferred !== undefined) {
759
+ for (const deferred of groupedFieldSet.deferred) {
760
+ const deferredLayerPlan = new LayerPlan_js_1.LayerPlan(this, outputPlan.layerPlan, {
761
+ type: "defer",
762
+ label: deferred.label,
763
+ });
764
+ const deferredOutputPlan = new OutputPlan_js_1.OutputPlan(deferredLayerPlan, outputPlan.rootStep, {
765
+ mode: "object",
766
+ deferLabel: deferred.label,
767
+ typeName: objectType.name,
768
+ },
769
+ // LOGGING: the location details should be tweaked to reference this
770
+ // fragment
771
+ outputPlan.locationDetails);
772
+ outputPlan.deferredOutputPlans.push(deferredOutputPlan);
773
+ this.processGroupedFieldSet(deferredOutputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, objectTypeFields, isMutation, deferred);
774
+ }
775
+ }
776
+ }
777
+ /**
778
+ *
779
+ * @param outputPlan - The output plan that this selection set is being added to
780
+ * @param path - The path within the outputPlan that we're adding stuff (only for root/object OutputPlans)
781
+ * @param parentStep - The step that represents the selection set root
782
+ * @param objectType - The object type that this selection set is being evaluated for (note polymorphic selection should already have been handled by this point)
783
+ * @param selections - The GraphQL selections (fields, fragment spreads, inline fragments) to evaluate
784
+ * @param isMutation - If true this selection set should be executed serially rather than in parallel (each field gets its own LayerPlan)
785
+ */
786
+ planSelectionSet(outputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, selections, isMutation = false) {
787
+ if (this.loc !== null) {
788
+ this.loc.push(`planSelectionSet(${objectType.name} @ ${outputPlan.layerPlan.id} @ ${path.join(".")} @ ${polymorphicPath ?? ""})`);
789
+ }
790
+ if (isDev) {
791
+ assertObjectType(objectType);
792
+ }
793
+ const groupedFieldSet = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(outputPlan.layerPlan, polymorphicPaths, graphqlCollectFields_js_1.graphqlCollectFields, null, this, parentStep.id, objectType, selections, isMutation);
794
+ const objectTypeFields = objectType.getFields();
795
+ this.processGroupedFieldSet(outputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, objectTypeFields, isMutation, groupedFieldSet);
796
+ if (this.loc !== null)
797
+ this.loc.pop();
798
+ }
799
+ // Similar to the old 'planFieldReturnType'
800
+ planIntoOutputPlan(parentOutputPlan,
801
+ // Typically this is parentOutputPlan.layerPlan; but in the case of mutationFields it isn't.
802
+ parentLayerPlan,
803
+ // This is the LAYER-RELATIVE path, not the absolute path! It resets!
804
+ path, polymorphicPath, polymorphicPaths, selections, parentObjectType, responseKey, fieldType, $step, locationDetails, listDepth = 0) {
805
+ const nullableFieldType = getNullableType(fieldType);
806
+ const isNonNull = nullableFieldType !== fieldType;
807
+ if (isListType(nullableFieldType)) {
808
+ const listOutputPlan = new OutputPlan_js_1.OutputPlan(parentLayerPlan, $step, OUTPUT_PLAN_TYPE_ARRAY, locationDetails);
809
+ parentOutputPlan.addChild(parentObjectType, responseKey, {
810
+ type: "outputPlan",
811
+ outputPlan: listOutputPlan,
812
+ isNonNull,
813
+ locationDetails,
814
+ });
815
+ const $__item = this.itemStepForListStep(parentLayerPlan, $step, listDepth);
816
+ const $item = (0, step_js_1.isListCapableStep)($step)
817
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)($__item.layerPlan, polymorphicPaths, $step.listItem, $step, $__item)
818
+ : $__item;
819
+ this.planIntoOutputPlan(listOutputPlan, $item.layerPlan, path, polymorphicPath, polymorphicPaths, selections, null, null, nullableFieldType.ofType, $item, locationDetails, listDepth + 1);
820
+ }
821
+ else if (isScalarType(nullableFieldType)) {
822
+ const scalarPlanResolver = nullableFieldType.extensions?.grafast?.plan;
823
+ if (scalarPlanResolver?.constructor?.name === "AsyncFunction") {
824
+ throw new Error(`Plans must be synchronous, but this schema has an async function at '${nullableFieldType.name}.plan': ${scalarPlanResolver.toString()}`);
825
+ }
826
+ const $leaf = typeof scalarPlanResolver === "function"
827
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(parentLayerPlan, polymorphicPaths, scalarPlanResolver, null, $step, this.scalarPlanInfo)
828
+ : $step;
829
+ parentOutputPlan.addChild(parentObjectType, responseKey, {
830
+ type: "outputPlan",
831
+ isNonNull,
832
+ outputPlan: new OutputPlan_js_1.OutputPlan(parentLayerPlan, $leaf, {
833
+ mode: "leaf",
834
+ // stepId: $leaf.id,
835
+ graphqlType: nullableFieldType,
836
+ }, locationDetails),
837
+ locationDetails,
838
+ });
839
+ }
840
+ else if (isEnumType(nullableFieldType)) {
841
+ parentOutputPlan.addChild(parentObjectType, responseKey, {
842
+ type: "outputPlan",
843
+ isNonNull,
844
+ outputPlan: new OutputPlan_js_1.OutputPlan(parentLayerPlan, $step, {
845
+ mode: "leaf",
846
+ graphqlType: nullableFieldType,
847
+ }, locationDetails),
848
+ locationDetails,
849
+ });
850
+ }
851
+ else if (isObjectType(nullableFieldType)) {
852
+ if (isDev) {
853
+ // Check that the plan we're dealing with is the one the user declared
854
+ /** Either an assertion function or a step class */
855
+ const stepAssertion = nullableFieldType.extensions?.grafast?.assertStep;
856
+ if (stepAssertion != null) {
857
+ try {
858
+ if (stepAssertion === index_js_1.ExecutableStep ||
859
+ stepAssertion.prototype instanceof index_js_1.ExecutableStep) {
860
+ if (!($step instanceof stepAssertion)) {
861
+ throw new Error(`Step mis-match: expected ${stepAssertion.name}, but instead found ${$step.constructor.name} (${$step})`);
862
+ }
863
+ }
864
+ else {
865
+ stepAssertion($step);
866
+ }
867
+ }
868
+ catch (e) {
869
+ throw new Error(`The step returned by '${path.join(".")}' is not compatible with the GraphQL object type '${nullableFieldType.name}': ${e.message}`, { cause: e });
870
+ }
871
+ }
872
+ if (!selections) {
873
+ throw new Error(`GrafastInternalError<7fe4f7d1-01d2-4f1e-add6-5aa6936938c9>: no selections on a GraphQLObjectType?!`);
874
+ }
875
+ }
876
+ let objectLayerPlan;
877
+ if (isNonNull ||
878
+ (parentLayerPlan.reason.type === "nullableBoundary" &&
879
+ parentLayerPlan.rootStep === $step)) {
880
+ objectLayerPlan = parentLayerPlan;
881
+ }
882
+ else {
883
+ // Find existing match
884
+ const match = parentLayerPlan.children.find((clp) => clp.reason.type === "nullableBoundary" &&
885
+ clp.reason.parentStep === $step);
886
+ if (match !== undefined) {
887
+ objectLayerPlan = match;
888
+ }
889
+ else {
890
+ objectLayerPlan = new LayerPlan_js_1.LayerPlan(this, parentLayerPlan, {
891
+ type: "nullableBoundary",
892
+ parentStep: $step,
893
+ });
894
+ objectLayerPlan.setRootStep($step);
895
+ }
896
+ }
897
+ const objectOutputPlan = new OutputPlan_js_1.OutputPlan(objectLayerPlan, $step, {
898
+ mode: "object",
899
+ deferLabel: undefined,
900
+ typeName: nullableFieldType.name,
901
+ }, locationDetails);
902
+ parentOutputPlan.addChild(parentObjectType, responseKey, {
903
+ type: "outputPlan",
904
+ outputPlan: objectOutputPlan,
905
+ isNonNull,
906
+ locationDetails,
907
+ });
908
+ this.planSelectionSet(objectOutputPlan, path, polymorphicPath, polymorphicPaths, $step, nullableFieldType, selections);
909
+ }
910
+ else {
911
+ // Polymorphic
912
+ const isUnion = isUnionType(nullableFieldType);
913
+ const isInterface = isInterfaceType(nullableFieldType);
914
+ if (!(isUnion || isInterface)) {
915
+ throw new Error(`GrafastInternalError<a54d6d63-d186-4ab9-9299-05f817894300>: Wasn't expecting ${nullableFieldType}`);
916
+ }
917
+ assert.ok(selections, "GrafastInternalError<d94e281c-1a10-463e-b7f5-2b0a3665d99b>: A polymorphic type with no selections is invalid");
918
+ /*
919
+ * Planning for polymorphic types is somewhat more complicated than for
920
+ * other types.
921
+ *
922
+ * First, we figure out the list of `possibleTypes` based on the
923
+ * union/interface and any other constraints that we know. NOTE: we can't
924
+ * discount a type just because it doesn't have any fragments that apply
925
+ * to it - instead we must still plan an empty selection set (or one just
926
+ * using `{__typename}`), and we need to know it exists for that. See
927
+ * https://github.com/graphql/graphql-spec/issues/951#issuecomment-1140957685
928
+ */
929
+ const allPossibleObjectTypes = isUnion
930
+ ? nullableFieldType.getTypes()
931
+ : this.schema.getImplementations(nullableFieldType).objects;
932
+ if (allPossibleObjectTypes.length === 0) {
933
+ /* If there are no implementations, simply stop via a null output plan */
934
+ const nullOutputPlan = new OutputPlan_js_1.OutputPlan(parentLayerPlan, $step, OUTPUT_PLAN_TYPE_NULL, locationDetails);
935
+ parentOutputPlan.addChild(parentObjectType, responseKey, {
936
+ type: "outputPlan",
937
+ outputPlan: nullOutputPlan,
938
+ isNonNull,
939
+ locationDetails,
940
+ });
941
+ }
942
+ else {
943
+ /*
944
+ * Next, we ensure we're dealing with a polymorphic step.
945
+ */
946
+ if (!(0, step_js_1.isPolymorphicStep)($step)) {
947
+ throw new Error(`${$step} is not a polymorphic capable step, it must have a planForType method`);
948
+ }
949
+ /*
950
+ * An output plan for it (knows how to branch the different object
951
+ * output plans).
952
+ */
953
+ const polymorphicOutputPlan = new OutputPlan_js_1.OutputPlan(parentLayerPlan, $step, {
954
+ mode: "polymorphic",
955
+ deferLabel: undefined,
956
+ typeNames: allPossibleObjectTypes.map((t) => t.name),
957
+ }, locationDetails);
958
+ parentOutputPlan.addChild(parentObjectType, responseKey, {
959
+ type: "outputPlan",
960
+ outputPlan: polymorphicOutputPlan,
961
+ isNonNull,
962
+ locationDetails,
963
+ });
964
+ /*
965
+ * Now a polymorphic layer plan for all the plans to live in
966
+ */
967
+ const polymorphicLayerPlan = this.getPolymorphicLayerPlan(parentLayerPlan, path, $step, allPossibleObjectTypes);
968
+ /*
969
+ * Now we need to loop through each type and plan it.
970
+ */
971
+ const polyBase = polymorphicPath ?? "";
972
+ const $oldStep = $step;
973
+ for (const type of allPossibleObjectTypes) {
974
+ /* TODO: this $oldStep / $step dance turned out to be necessary
975
+ * because `$step` was being replaced in the following query against
976
+ * the pggql_test `polymorpic` schema; but I'm not sure this is the
977
+ * right behavior - shouldn't deduplication have been temporarily
978
+ * disabled during this?
979
+ *
980
+ * ```graphql
981
+ * {
982
+ * allSingleTableItems {
983
+ * nodes {
984
+ * id
985
+ * type
986
+ * singleTableItemsByParentIdList {
987
+ * id
988
+ * position
989
+ * }
990
+ * }
991
+ * }
992
+ * }
993
+ * ```
994
+ */
995
+ const $step = this.stepTracker.getStepById($oldStep.id);
996
+ // Bit of a hack, but saves passing it around through all the arguments
997
+ const newPolymorphicPath = `${polyBase}>${type.name}`;
998
+ polymorphicLayerPlan.reason.polymorphicPaths.add(newPolymorphicPath);
999
+ const newPolymorphicPaths = new Set();
1000
+ newPolymorphicPaths.add(newPolymorphicPath);
1001
+ const $root = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(polymorphicLayerPlan, newPolymorphicPaths, $step.planForType, $step, type);
1002
+ const objectOutputPlan = new OutputPlan_js_1.OutputPlan(polymorphicLayerPlan, $root, {
1003
+ mode: "object",
1004
+ deferLabel: undefined,
1005
+ typeName: type.name,
1006
+ }, locationDetails);
1007
+ // find all selections compatible with `type`
1008
+ const fieldNodes = (0, graphqlMergeSelectionSets_js_1.fieldSelectionsForType)(this, type, selections);
1009
+ this.planSelectionSet(objectOutputPlan, path, newPolymorphicPath, newPolymorphicPaths, $root, type, fieldNodes, false);
1010
+ polymorphicOutputPlan.addChild(type, null, {
1011
+ type: "outputPlan",
1012
+ isNonNull,
1013
+ outputPlan: objectOutputPlan,
1014
+ locationDetails,
1015
+ });
1016
+ }
1017
+ }
1018
+ }
1019
+ }
1020
+ getPolymorphicLayerPlan(parentLayerPlan, path, $step, allPossibleObjectTypes) {
1021
+ // OPTIMIZE: I added the $step.id to pathString to fix a planning issue; but maybe we can do this without branching?
1022
+ // https://github.com/benjie/crystal/issues/109
1023
+ const pathString = `${path.join("|")}!${$step.id}`;
1024
+ const polymorphicLayerPlanByPath = this.polymorphicLayerPlanByPathByLayerPlan.get(parentLayerPlan) ??
1025
+ new Map();
1026
+ if (polymorphicLayerPlanByPath.size === 0) {
1027
+ this.polymorphicLayerPlanByPathByLayerPlan.set(parentLayerPlan, polymorphicLayerPlanByPath);
1028
+ }
1029
+ const prev = polymorphicLayerPlanByPath.get(pathString);
1030
+ if (prev !== undefined) {
1031
+ // NOTE: this is typically hit when you have a polymorphic field inside
1032
+ // another polymorphic field - in these cases rather than having the
1033
+ // polymorphism multiply out, we can aim for fewer polymorphic buckets
1034
+ // keeping the plan simpler (and more efficient).
1035
+ const { stepId, layerPlan } = prev;
1036
+ const stepByStepId = this.stepTracker.getStepById(stepId);
1037
+ const stepBy$stepId = this.stepTracker.getStepById($step.id);
1038
+ if (stepByStepId !== stepBy$stepId) {
1039
+ throw new Error(`GrafastInternalError<e01bdc40-7c89-41c6-8d84-56efa22c872a>: unexpected inconsistency when determining the polymorphic LayerPlan to use (pathString = ${pathString}, ${stepByStepId} (${stepId}) != ${stepBy$stepId} (${$step.id}))`);
1040
+ }
1041
+ for (const t of allPossibleObjectTypes) {
1042
+ if (!layerPlan.reason.typeNames.includes(t.name)) {
1043
+ // Since we're re-using an existing LayerPlan, we should be careful to
1044
+ // ensure none of the previous assumptions have been broken.
1045
+ layerPlan.reason.typeNames.push(t.name);
1046
+ }
1047
+ }
1048
+ return layerPlan;
1049
+ }
1050
+ else {
1051
+ const layerPlan = new LayerPlan_js_1.LayerPlan(this, parentLayerPlan, {
1052
+ type: "polymorphic",
1053
+ typeNames: allPossibleObjectTypes.map((t) => t.name),
1054
+ parentStep: $step,
1055
+ polymorphicPaths: new Set(),
1056
+ });
1057
+ polymorphicLayerPlanByPath.set(pathString, {
1058
+ stepId: $step.id,
1059
+ layerPlan,
1060
+ });
1061
+ return layerPlan;
1062
+ }
1063
+ }
1064
+ planField(layerPlan, path, polymorphicPaths, fieldNodes, planResolver, applyAfterMode, rawParentStep, field, trackedArguments, deduplicate = true) {
1065
+ // The step may have been de-duped whilst sibling steps were planned
1066
+ // PERF: this should be handled in the parent?
1067
+ const parentStep = this.stepTracker.getStepById(rawParentStep.id);
1068
+ const previousStepCount = this.stepTracker.stepCount;
1069
+ if (this.loc !== null)
1070
+ this.loc.push(`planField(${path.join(".")})`);
1071
+ try {
1072
+ let step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, operationPlan_input_js_1.withFieldArgsForArguments, null, this, parentStep, trackedArguments, field, applyAfterMode, (fieldArgs) => planResolver(parentStep, fieldArgs, {
1073
+ field,
1074
+ schema: this.schema,
1075
+ }));
1076
+ let haltTree = false;
1077
+ if (step === null || (step instanceof constant_js_1.ConstantStep && step.isNull())) {
1078
+ // Constantly null; do not step any further in this tree.
1079
+ step =
1080
+ step ||
1081
+ // `withGlobalLayerPlan(layerPlan, polymorphicPaths, () => constant(null))` but with reduced memory allocation
1082
+ (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, constant_js_1.constant, null, null);
1083
+ haltTree = true;
1084
+ }
1085
+ (0, step_js_1.assertExecutableStep)(step);
1086
+ // TODO: Check SameStreamDirective still exists in @stream spec at release.
1087
+ /*
1088
+ * `SameStreamDirective`
1089
+ * (https://github.com/graphql/graphql-spec/blob/26fd78c4a89a79552dcc0c7e0140a975ce654400/spec/Section%205%20--%20Validation.md#L450-L458)
1090
+ * ensures that every field that has `@stream` must have the same
1091
+ * `@stream` arguments; so we can just check the first node in the
1092
+ * merged set to see our stream options. NOTE: if this changes before
1093
+ * release then we may need to find the stream with the largest
1094
+ * `initialCount` to figure what to do; something like:
1095
+ *
1096
+ * const streamDirective = firstField.directives?.filter(
1097
+ * (d) => d.name.value === "stream",
1098
+ * ).sort(
1099
+ * (a, z) => getArg(z, 'initialCount', 0) - getArg(a, 'initialCount', 0)
1100
+ * )[0]
1101
+ */
1102
+ const streamDirective = fieldNodes[0].directives?.find((d) => d.name.value === "stream");
1103
+ const stepOptions = {
1104
+ stream: !haltTree && streamDirective
1105
+ ? {
1106
+ initialCount: Number((0, graphqlCollectFields_js_1.evalDirectiveArg)(fieldNodes[0], "stream", "initialCount", this.trackedVariableValuesStep, null)) || 0,
1107
+ }
1108
+ : null,
1109
+ };
1110
+ step._stepOptions = stepOptions;
1111
+ if (deduplicate) {
1112
+ // Now that the field has been planned (including arguments, but NOT
1113
+ // including selection set) we can deduplicate it to see if any of its
1114
+ // peers are identical.
1115
+ this.deduplicateSteps();
1116
+ // After deduplication, this step may have been substituted; get the
1117
+ // updated reference.
1118
+ step = this.stepTracker.getStepById(step.id);
1119
+ }
1120
+ return { step, haltTree };
1121
+ }
1122
+ catch (e) {
1123
+ if (ALWAYS_THROW_PLANNING_ERRORS) {
1124
+ throw e;
1125
+ }
1126
+ if (THROW_PLANNING_ERRORS_ON_SIDE_EFFECTS) {
1127
+ for (let i = previousStepCount; i < this.stepTracker.stepCount; i++) {
1128
+ const step = this.stepTracker.stepById[i];
1129
+ if (step && step.hasSideEffects) {
1130
+ throw e;
1131
+ }
1132
+ }
1133
+ }
1134
+ try {
1135
+ this.stepTracker.purgeBackTo(previousStepCount);
1136
+ }
1137
+ catch (e2) {
1138
+ console.error(`Cleanup error occurred whilst trying to recover from field planning error: ${e2.stack}`);
1139
+ throw e;
1140
+ }
1141
+ const step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, index_js_1.error, null, e);
1142
+ const haltTree = true;
1143
+ this.modifierSteps = [];
1144
+ // PERF: consider deleting all steps that were allocated during this. For
1145
+ // now we'll just rely on tree-shaking.
1146
+ return { step, haltTree };
1147
+ }
1148
+ finally {
1149
+ if (this.loc !== null)
1150
+ this.loc.pop();
1151
+ }
1152
+ }
1153
+ /**
1154
+ * A replacement for GraphQL's
1155
+ * `CoerceArgumentValues` that factors in tracked variables.
1156
+ *
1157
+ * @see https://spec.graphql.org/draft/#CoerceArgumentValues()
1158
+ */
1159
+ getTrackedArguments(argumentDefinitions, field) {
1160
+ const argumentValues = field.arguments;
1161
+ const trackedArgumentValues = Object.create(null);
1162
+ for (const argumentDefinition of argumentDefinitions) {
1163
+ const argumentName = argumentDefinition.name;
1164
+ if (isDev && trackedArgumentValues[argumentName]) {
1165
+ throw new index_js_1.SafeError(`Argument name '${argumentName}' seen twice; aborting.`);
1166
+ }
1167
+ const argumentType = argumentDefinition.type;
1168
+ const defaultValue = argumentDefinition.defaultValue
1169
+ ? (0, utils_js_1.defaultValueToValueNode)(argumentType, argumentDefinition.defaultValue)
1170
+ : undefined;
1171
+ const argumentValue = argumentValues?.find((v) => v.name.value === argumentName);
1172
+ const argumentPlan = (0, input_js_1.inputStep)(this, argumentType, argumentValue?.value, defaultValue);
1173
+ trackedArgumentValues[argumentName] = argumentPlan;
1174
+ }
1175
+ return {
1176
+ get(name) {
1177
+ return trackedArgumentValues[name];
1178
+ },
1179
+ };
1180
+ }
1181
+ withModifiers(cb) {
1182
+ // Stash previous modifiers
1183
+ const previousModifierDepthCount = this.modifierDepthCount++;
1184
+ const previousCount = this.modifierStepCount;
1185
+ const previousStack = this.modifierSteps.splice(0, this.modifierSteps.length);
1186
+ this.modifierStepCount = 0;
1187
+ let result;
1188
+ let plansToApply;
1189
+ try {
1190
+ result = cb();
1191
+ }
1192
+ finally {
1193
+ // Remove the modifier plans from operationPlan and sort them ready for application.
1194
+ plansToApply = this.modifierSteps
1195
+ .splice(0, this.modifierSteps.length)
1196
+ .reverse();
1197
+ // Restore previous modifiers
1198
+ this.modifierStepCount = previousCount;
1199
+ for (const mod of previousStack) {
1200
+ this.modifierSteps.push(mod);
1201
+ }
1202
+ // Restore previous depth
1203
+ this.modifierDepthCount = previousModifierDepthCount;
1204
+ }
1205
+ // Apply the plans.
1206
+ for (let i = 0, l = plansToApply.length; i < l; i++) {
1207
+ plansToApply[i].apply();
1208
+ }
1209
+ return result;
1210
+ }
1211
+ /**
1212
+ * Sets up tracking for the given value (variableValues, context, rootValue).
1213
+ */
1214
+ track(value, constraints, variableDefinitions) {
1215
+ const valueStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, newValueStepCallback);
1216
+ const trackedObjectStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => new index_js_1.__TrackedValueStep(value, valueStep, constraints, [], variableDefinitions));
1217
+ return [valueStep, trackedObjectStep];
1218
+ }
1219
+ /**
1220
+ * Checks that no step has a property on it whose value is another step. It
1221
+ * should addDependency instead.
1222
+ */
1223
+ validateSteps(offset = 0) {
1224
+ const errors = [];
1225
+ for (const step of this.stepTracker.activeSteps) {
1226
+ if (step.id < offset)
1227
+ continue;
1228
+ for (const key in step) {
1229
+ const val = step[key];
1230
+ if (val instanceof index_js_1.ExecutableStep) {
1231
+ errors.push(new Error(`ERROR: ExecutableStep ${step} has illegal reference via property '${key}' to step ${val}. You must not reference steps directly, instead add the step as a dependency (\`depId = this.addDependency($step)\`) and use \`this.getDep(depId)\` to look the step up (or, if the step is not a dependency, store the step ID and use \`this.getStep(stepId)\` each time you need to reference it). Failure to comply could result in subtle breakage during optimisation.`));
1232
+ }
1233
+ }
1234
+ }
1235
+ if (errors.length > 0) {
1236
+ console.error(errors.map((e) => e.message).join("\n"));
1237
+ throw errors[0];
1238
+ }
1239
+ }
1240
+ replaceStep($original, $replacement) {
1241
+ this.stepTracker.replaceStep($original, $replacement);
1242
+ }
1243
+ processStep(actionDescription, order, isReadonly, callback, processed, step) {
1244
+ // MUST come before anything else, otherwise infinite loops may occur
1245
+ processed.add(step);
1246
+ if (!this.stepTracker.activeSteps.has(step)) {
1247
+ return step;
1248
+ }
1249
+ if (order === "dependents-first") {
1250
+ // used for: pushDown, optimize
1251
+ for (let i = 0; i < step.dependents.length; i++) {
1252
+ const entry = step.dependents[i];
1253
+ const { step: $processFirst } = entry;
1254
+ if (!processed.has($processFirst)) {
1255
+ this.processStep(actionDescription, order, isReadonly, callback, processed, $processFirst);
1256
+ if (step.dependents[i] !== entry) {
1257
+ // The world has change; go back to the start
1258
+ i = -1;
1259
+ }
1260
+ }
1261
+ }
1262
+ const subroutineLayerPlan = step[interfaces_js_1.$$subroutine];
1263
+ if (subroutineLayerPlan !== null) {
1264
+ const $root = subroutineLayerPlan.rootStep;
1265
+ if ($root) {
1266
+ this.processStep(actionDescription, order, isReadonly, callback, processed, $root);
1267
+ }
1268
+ }
1269
+ }
1270
+ else {
1271
+ // used for: hoist
1272
+ for (const $processFirst of step.dependencies) {
1273
+ if (!processed.has($processFirst)) {
1274
+ this.processStep(actionDescription, order, isReadonly, callback, processed, $processFirst);
1275
+ }
1276
+ }
1277
+ }
1278
+ // Check again, processing another step may have invalidated this one.
1279
+ if (!this.stepTracker.activeSteps.has(step)) {
1280
+ return step;
1281
+ }
1282
+ let replacementStep = step;
1283
+ try {
1284
+ replacementStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, callback, this, step);
1285
+ }
1286
+ catch (e) {
1287
+ console.error(`Error occurred during ${actionDescription}; whilst processing ${step} in ${order} mode an error occurred:`, e);
1288
+ throw e;
1289
+ }
1290
+ if (!replacementStep) {
1291
+ throw new Error(`The callback did not return a step during ${actionDescription}`);
1292
+ }
1293
+ if (isReadonly) {
1294
+ if (replacementStep !== step) {
1295
+ throw new Error(`Replacing step in readonly mode is not permitted!`);
1296
+ }
1297
+ }
1298
+ else {
1299
+ if (replacementStep !== step) {
1300
+ this.replaceStep(step, replacementStep);
1301
+ }
1302
+ this.deduplicateSteps();
1303
+ }
1304
+ return replacementStep;
1305
+ }
1306
+ // PERF: optimize
1307
+ /**
1308
+ * Process the given steps, either dependencies first (root to leaf) or
1309
+ * dependents first (leaves to root).
1310
+ *
1311
+ * @internal
1312
+ */
1313
+ processSteps(actionDescription, order, isReadonly, callback) {
1314
+ const previousStepCount = this.stepTracker.stepCount;
1315
+ const processed = new Set();
1316
+ for (let i = 0; i < this.stepTracker.stepCount; i++) {
1317
+ const step = this.stepTracker.getStepById(i, true);
1318
+ // Check it hasn't been tree-shaken away, or already processed
1319
+ if (!step || step.id !== i || processed.has(step))
1320
+ continue;
1321
+ const resultStep = this.processStep(actionDescription, order, isReadonly, callback, processed, step);
1322
+ if (isDev) {
1323
+ const plansAdded = this.stepTracker.stepCount - previousStepCount;
1324
+ // NOTE: whilst processing steps new steps may be added, thus we must loop
1325
+ // ascending and we must re-evaluate this.stepTracker.stepCount on each loop
1326
+ // iteration.
1327
+ if (plansAdded > 100000) {
1328
+ throw new Error(`Whilst processing steps as part of ${actionDescription}Plans, ${plansAdded} new steps have been created... That seems like it's likely a bug in the relevant method of one of your steps. The last plan processed was ${resultStep}`);
1329
+ }
1330
+ }
1331
+ if (isReadonly && this.stepTracker.stepCount > previousStepCount) {
1332
+ throwNoNewStepsError(this, actionDescription, step, previousStepCount, "Creating steps in isReadonly mode is forbidden");
1333
+ }
1334
+ }
1335
+ if (!isReadonly &&
1336
+ this.phase !== "plan" &&
1337
+ this.stepTracker.stepCount > previousStepCount) {
1338
+ // Any time new steps are added we should validate them. All plans are
1339
+ // validated once "plan" is finished, so no need to do it here for that
1340
+ // phase.
1341
+ this.validateSteps(previousStepCount);
1342
+ }
1343
+ }
1344
+ /**
1345
+ * Peers are steps of the same type (but not the same step!) that are in
1346
+ * compatible layers and have the same dependencies. Peers must not have side
1347
+ * effects. A step is not its own peer.
1348
+ */
1349
+ getPeers(step) {
1350
+ if (step.hasSideEffects) {
1351
+ return EMPTY_ARRAY;
1352
+ }
1353
+ const { dependencies: deps, layerPlan: layerPlan, constructor: stepConstructor, } = step;
1354
+ const dependencyCount = deps.length;
1355
+ if (dependencyCount === 0) {
1356
+ let allPeers = null;
1357
+ for (const possiblyPeer of this.stepTracker.stepsWithNoDependencies) {
1358
+ if (possiblyPeer !== step &&
1359
+ !possiblyPeer.hasSideEffects &&
1360
+ possiblyPeer.layerPlan === layerPlan &&
1361
+ possiblyPeer.constructor === stepConstructor) {
1362
+ if (allPeers === null) {
1363
+ allPeers = [possiblyPeer];
1364
+ }
1365
+ else {
1366
+ allPeers.push(possiblyPeer);
1367
+ }
1368
+ }
1369
+ }
1370
+ return allPeers === null ? EMPTY_ARRAY : allPeers;
1371
+ }
1372
+ else if (dependencyCount === 1) {
1373
+ // Optimized form for steps that have one dependency (extremely common!)
1374
+ const { ancestry, deferBoundaryDepth } = layerPlan;
1375
+ const dep = deps[0];
1376
+ const dl = dep.dependents.length;
1377
+ if (dl === 1) {
1378
+ // We're the only dependent; therefore we have no peers (since peers
1379
+ // share dependencies)
1380
+ return EMPTY_ARRAY;
1381
+ }
1382
+ const minDepth = Math.max(deferBoundaryDepth, dep.layerPlan.depth);
1383
+ let allPeers = null;
1384
+ for (const { dependencyIndex: peerDependencyIndex, step: possiblyPeer, } of dep.dependents) {
1385
+ const { layerPlan: peerLayerPlan } = possiblyPeer;
1386
+ if (possiblyPeer !== step &&
1387
+ peerDependencyIndex === 0 &&
1388
+ !possiblyPeer.hasSideEffects &&
1389
+ possiblyPeer.constructor === stepConstructor &&
1390
+ peerLayerPlan.depth >= minDepth &&
1391
+ possiblyPeer.dependencies.length === dependencyCount &&
1392
+ peerLayerPlan === ancestry[peerLayerPlan.depth]) {
1393
+ if (allPeers === null) {
1394
+ allPeers = [possiblyPeer];
1395
+ }
1396
+ else {
1397
+ allPeers.push(possiblyPeer);
1398
+ }
1399
+ }
1400
+ }
1401
+ return allPeers === null ? EMPTY_ARRAY : allPeers;
1402
+ }
1403
+ else {
1404
+ const { ancestry, deferBoundaryDepth } = layerPlan;
1405
+ /**
1406
+ * "compatible" layer plans are calculated by walking up the layer plan tree,
1407
+ * however:
1408
+ *
1409
+ * - do not pass the LayerPlan of one of the dependencies
1410
+ * - do not pass a "deferred" layer plan
1411
+ *
1412
+ * Compatible layer plans are no less deep than minDepth.
1413
+ */
1414
+ let minDepth = deferBoundaryDepth;
1415
+ const possiblePeers = [];
1416
+ for (let dependencyIndex = 0; dependencyIndex < dependencyCount; dependencyIndex++) {
1417
+ const dep = deps[dependencyIndex];
1418
+ const dl = dep.dependents.length;
1419
+ if (dl === 1) {
1420
+ // We're the only dependent; therefore we have no peers (since peers
1421
+ // share dependencies)
1422
+ return EMPTY_ARRAY;
1423
+ }
1424
+ if (dependencyIndex === dependencyCount - 1) {
1425
+ // Check the final dependency - this is likely to have the fewest
1426
+ // dependents (since it was added last it's more likely to be
1427
+ // unique).
1428
+ for (const { dependencyIndex: peerDependencyIndex, step: possiblyPeer, } of dep.dependents) {
1429
+ if (possiblyPeer !== step &&
1430
+ peerDependencyIndex === dependencyIndex &&
1431
+ !possiblyPeer.hasSideEffects &&
1432
+ possiblyPeer.constructor === stepConstructor &&
1433
+ possiblyPeer.dependencies.length === dependencyCount &&
1434
+ possiblyPeer.layerPlan === ancestry[possiblyPeer.layerPlan.depth]) {
1435
+ possiblePeers.push(possiblyPeer);
1436
+ }
1437
+ }
1438
+ }
1439
+ const d = dep.layerPlan.depth;
1440
+ if (d > minDepth) {
1441
+ minDepth = d;
1442
+ }
1443
+ }
1444
+ if (possiblePeers.length === 0) {
1445
+ return EMPTY_ARRAY;
1446
+ }
1447
+ let allPeers = null;
1448
+ outerloop: for (const possiblyPeer of possiblePeers) {
1449
+ if (possiblyPeer.layerPlan.depth < minDepth)
1450
+ continue;
1451
+ // We know the final dependency matches and the dependency count
1452
+ // matches - check the other dependencies match.
1453
+ for (let i = 0; i < dependencyCount - 1; i++) {
1454
+ if (deps[i] !== possiblyPeer.dependencies[i]) {
1455
+ continue outerloop;
1456
+ }
1457
+ }
1458
+ if (allPeers === null) {
1459
+ allPeers = [possiblyPeer];
1460
+ }
1461
+ else {
1462
+ allPeers.push(possiblyPeer);
1463
+ }
1464
+ }
1465
+ return allPeers === null ? EMPTY_ARRAY : allPeers;
1466
+ }
1467
+ }
1468
+ isImmoveable(step) {
1469
+ if (step.hasSideEffects) {
1470
+ return true;
1471
+ }
1472
+ if (isPrepopulatedStep(step)) {
1473
+ return true;
1474
+ }
1475
+ if (step[interfaces_js_1.$$subroutine] !== null) {
1476
+ // Don't hoist steps that are the parent of a subroutine
1477
+ // PERF: we _should_ be able to hoist, but care must be taken. Currently it causes test failures.
1478
+ return true;
1479
+ }
1480
+ return false;
1481
+ }
1482
+ /**
1483
+ * Attempts to hoist the step into a higher layerPlan to maximize
1484
+ * deduplication.
1485
+ */
1486
+ hoistStep(step) {
1487
+ if (this.isImmoveable(step)) {
1488
+ return;
1489
+ }
1490
+ // PERF: would be nice to prevent ConstantStep from being hoisted - we
1491
+ // don't want to keep multiplying up and up its data as it traverses the buckets - would be better
1492
+ // to push the step down to the furthest level and then have it run there straight away.
1493
+ // PERF: actually... might be better to specifically replace all
1494
+ // ConstantStep dependencies with a bucket-local ConstantStep as one of the
1495
+ // final steps of optimize.
1496
+ /* // This is disabled because it breaks pgSelect's `mergePlaceholdersInto` logic.
1497
+ if (step instanceof __InputStaticLeafStep || step instanceof ConstantStep) {
1498
+ // More optimal to not hoist these - they convert to `constant()` which executes to produce a filled array incredibly efficiently.
1499
+ return true;
1500
+ }
1501
+ */
1502
+ if (step.layerPlan.parentLayerPlan?.reason.type === "mutationField") {
1503
+ // Never hoist into a mutation layer
1504
+ return;
1505
+ }
1506
+ switch (step.layerPlan.reason.type) {
1507
+ case "root": {
1508
+ // There is no higher layerPlan
1509
+ return;
1510
+ }
1511
+ case "subscription":
1512
+ case "defer": {
1513
+ // Should be deferred, don't evaluate early (unless it's cheap to do so)
1514
+ if (step.isSyncAndSafe) {
1515
+ // It's cheap, try and hoist it
1516
+ break;
1517
+ }
1518
+ else {
1519
+ return;
1520
+ }
1521
+ }
1522
+ case "polymorphic": {
1523
+ // May only need to be evaluated for certain types, so avoid hoisting anything expensive.
1524
+ if (step.isSyncAndSafe &&
1525
+ step.polymorphicPaths.size ===
1526
+ step.layerPlan.reason.polymorphicPaths.size) {
1527
+ // It's cheap and covers all types, try and hoist it.
1528
+ // NOTE: I have concerns about whether this is safe or not, but I
1529
+ // have not been able to come up with a counterexample that is
1530
+ // unsafe. Should we do so, we should remove this.
1531
+ break;
1532
+ }
1533
+ else {
1534
+ return;
1535
+ }
1536
+ }
1537
+ case "subroutine": {
1538
+ // Should be safe to hoist.
1539
+ break;
1540
+ }
1541
+ case "nullableBoundary": {
1542
+ // Safe to hoist _unless_ it depends on the root step of the nullableBoundary.
1543
+ const $root = step.layerPlan.reason.parentStep;
1544
+ if (step.dependencies.includes($root)) {
1545
+ return;
1546
+ }
1547
+ else {
1548
+ break;
1549
+ }
1550
+ }
1551
+ case "listItem": {
1552
+ // Should be safe to hoist so long as it doesn't depend on the
1553
+ // `__ItemStep` itself (which is just a regular dependency, so it'll be
1554
+ // checked later).
1555
+ // NOTE: this is true even if the listItem streams.
1556
+ break;
1557
+ }
1558
+ case "mutationField": {
1559
+ // NOTE: It's the user's responsibility to ensure that steps that have
1560
+ // side effects are marked as such via `step.hasSideEffects = true`.
1561
+ if (step.isSyncAndSafe && !step.hasSideEffects) {
1562
+ if (step.layerPlan.reason.mutationIndex === 0) {
1563
+ // Safe to hoist inside first mutation; but all later mutations may be impacted by previous actions.
1564
+ break;
1565
+ }
1566
+ // OPTIMIZE: figure out under which circumstances it is safe to hoist here.
1567
+ // break;
1568
+ return;
1569
+ }
1570
+ else {
1571
+ // Plans that rely on external state shouldn't be hoisted because
1572
+ // their results may change after a mutation, so the mutation should
1573
+ // run first.
1574
+ return;
1575
+ }
1576
+ }
1577
+ default: {
1578
+ const never = step.layerPlan.reason;
1579
+ throw new Error(`GrafastInternalError<81e3a7d4-aaa0-416b-abbb-a887734007bc>: unhandled layer plan reason ${(0, inspect_js_1.inspect)(never)}`);
1580
+ }
1581
+ }
1582
+ // Finally, check that none of its dependencies are in the same bucket.
1583
+ const deps = step.dependencies;
1584
+ if (deps.some((dep) => dep.layerPlan === step.layerPlan)) {
1585
+ return;
1586
+ }
1587
+ // All our checks passed, hoist it.
1588
+ assert.ok(step.layerPlan.parentLayerPlan !== null, "GrafastInternalError<55c8940f-e8ac-4985-8b34-96fc6f81d62d>: A non-root layer plan had no parent?!");
1589
+ // 1: adjust polymorphicPaths to fit new layerPlan
1590
+ if (step.layerPlan.reason.type === "polymorphic") {
1591
+ // PERF: this is cacheable
1592
+ /** The closest ancestor layer plan that is polymorphic */
1593
+ let ancestor = step.layerPlan;
1594
+ while ((ancestor = ancestor.parentLayerPlan)) {
1595
+ if (ancestor.reason.type === "polymorphic") {
1596
+ break;
1597
+ }
1598
+ }
1599
+ const parentPolymorphicPaths = ancestor
1600
+ ? ancestor.reason
1601
+ .polymorphicPaths
1602
+ : exports.POLYMORPHIC_ROOT_PATHS;
1603
+ const myPaths = [...step.polymorphicPaths];
1604
+ if (parentPolymorphicPaths?.has(myPaths[0])) {
1605
+ // All the others must be valid too
1606
+ }
1607
+ else if (parentPolymorphicPaths === null) {
1608
+ step.polymorphicPaths = null;
1609
+ }
1610
+ else {
1611
+ const layerPaths = [...step.layerPlan.reason.polymorphicPaths];
1612
+ const newPaths = new Set();
1613
+ for (const path of parentPolymorphicPaths) {
1614
+ const prefix = path + ">";
1615
+ const matches = myPaths.filter((p) => p.startsWith(prefix));
1616
+ const layerMatches = layerPaths.filter((p) => p.startsWith(prefix));
1617
+ if (matches.length !== layerMatches.length) {
1618
+ // Can't hoist because it's not used for all polymorphicPaths of this type
1619
+ return;
1620
+ }
1621
+ else if (matches.length > 0) {
1622
+ newPaths.add(path);
1623
+ }
1624
+ }
1625
+ step.polymorphicPaths = newPaths;
1626
+ }
1627
+ }
1628
+ const $subroutine = step.layerPlan.reason.type === "subroutine"
1629
+ ? step.layerPlan.reason.parentStep
1630
+ : null;
1631
+ // 2: move it up a layer
1632
+ this.stepTracker.moveStepToLayerPlan(step, step.layerPlan.parentLayerPlan);
1633
+ // 3: if it's was in a subroutine, the subroutine parent plan needs to list it as a dependency
1634
+ if ($subroutine) {
1635
+ // Naughty naughty
1636
+ $subroutine.addDependency(step);
1637
+ }
1638
+ // Now try and hoist it again!
1639
+ this.hoistStep(step);
1640
+ }
1641
+ /**
1642
+ * Attempts to push the step into the lowest layerPlan to minimize the need
1643
+ * for copying between layer plans.
1644
+ */
1645
+ pushDown(step) {
1646
+ if (this.isImmoveable(step)) {
1647
+ return step;
1648
+ }
1649
+ switch (step.layerPlan.reason.type) {
1650
+ case "root":
1651
+ case "subscription":
1652
+ case "defer":
1653
+ case "polymorphic":
1654
+ case "subroutine":
1655
+ case "nullableBoundary":
1656
+ case "listItem": {
1657
+ // Fine to push lower
1658
+ break;
1659
+ }
1660
+ case "mutationField": {
1661
+ // NOTE: It's the user's responsibility to ensure that steps that have
1662
+ // side effects are marked as such via `step.hasSideEffects = true`.
1663
+ if (step.isSyncAndSafe && !step.hasSideEffects) {
1664
+ break;
1665
+ }
1666
+ else {
1667
+ // Side effects should take place inside the mutation field plan
1668
+ // (that's the whole point), so we should not push these down.
1669
+ return step;
1670
+ }
1671
+ }
1672
+ default: {
1673
+ const never = step.layerPlan.reason;
1674
+ throw new Error(`GrafastInternalError<81e3a7d4-aaa0-416b-abbb-a887734009bc>: unhandled layer plan reason ${(0, inspect_js_1.inspect)(never)}`);
1675
+ }
1676
+ }
1677
+ // Now find the lowest bucket that still satisfies all of it's dependents.
1678
+ const dependentLayerPlans = new Set();
1679
+ const outputPlans = this.stepTracker.outputPlansByRootStep.get(step);
1680
+ if (outputPlans !== undefined) {
1681
+ for (const outputPlan of outputPlans) {
1682
+ if (outputPlan.layerPlan === step.layerPlan) {
1683
+ return step;
1684
+ }
1685
+ else {
1686
+ dependentLayerPlans.add(outputPlan.layerPlan);
1687
+ }
1688
+ }
1689
+ }
1690
+ for (const { step: s } of step.dependents) {
1691
+ if (s.layerPlan === step.layerPlan) {
1692
+ return step;
1693
+ }
1694
+ else {
1695
+ dependentLayerPlans.add(s.layerPlan);
1696
+ }
1697
+ }
1698
+ const layerPlansByParent = this.stepTracker.layerPlansByParentStep.get(step);
1699
+ if (layerPlansByParent !== undefined) {
1700
+ for (const layerPlan of layerPlansByParent) {
1701
+ if (layerPlan.parentLayerPlan === step.layerPlan) {
1702
+ return step;
1703
+ }
1704
+ else {
1705
+ dependentLayerPlans.add(layerPlan.parentLayerPlan);
1706
+ }
1707
+ }
1708
+ }
1709
+ const layerPlansByRoot = this.stepTracker.layerPlansByRootStep.get(step);
1710
+ if (layerPlansByRoot !== undefined) {
1711
+ for (const layerPlan of layerPlansByRoot) {
1712
+ if (layerPlan === step.layerPlan) {
1713
+ return step;
1714
+ }
1715
+ else {
1716
+ dependentLayerPlans.add(layerPlan);
1717
+ }
1718
+ }
1719
+ }
1720
+ if (dependentLayerPlans.size === 0) {
1721
+ throw new Error(`Nothing depends on ${step}?!`);
1722
+ }
1723
+ if (dependentLayerPlans.has(step.layerPlan)) {
1724
+ throw new Error(`GrafastInternalError<c5cefdbc-9aa4-4895-8966-71171d8c0b36>: This should already have been caught`);
1725
+ }
1726
+ const paths = [];
1727
+ let minPathLength = Infinity;
1728
+ for (const dependentLayerPlan of dependentLayerPlans) {
1729
+ let lp = dependentLayerPlan;
1730
+ const path = [lp];
1731
+ while (lp.parentLayerPlan != step.layerPlan) {
1732
+ const parent = lp.parentLayerPlan;
1733
+ if (!parent) {
1734
+ throw new Error(`GrafastInternalError<64c07427-4fe2-43c4-9858-272d33bee0b8>: invalid layer plan heirarchy`);
1735
+ }
1736
+ lp = parent;
1737
+ path.push(lp);
1738
+ }
1739
+ paths.push(path);
1740
+ minPathLength = Math.min(path.length, minPathLength);
1741
+ }
1742
+ const dependentLayerPlanCount = dependentLayerPlans.size;
1743
+ let deepest = step.layerPlan;
1744
+ outerloop: for (let i = 0; i < minPathLength; i++) {
1745
+ const expected = paths[0][i];
1746
+ if (expected.reason.type === "polymorphic") {
1747
+ // PERF: reconsider
1748
+ // Let's not pass polymorphic boundaries for now
1749
+ break;
1750
+ }
1751
+ if (expected.reason.type === "subroutine") {
1752
+ // PERF: reconsider
1753
+ // Let's not pass subroutine boundaries for now
1754
+ break;
1755
+ }
1756
+ if (expected.reason.type === "mutationField") {
1757
+ // Let's not pass mutationField boundaries for now
1758
+ break;
1759
+ }
1760
+ for (let j = 1; j < dependentLayerPlanCount; j++) {
1761
+ const actual = paths[j][i];
1762
+ if (expected != actual) {
1763
+ break outerloop;
1764
+ }
1765
+ }
1766
+ deepest = expected;
1767
+ }
1768
+ if (deepest === step.layerPlan) {
1769
+ return step;
1770
+ }
1771
+ // All our checks passed, shove it down!
1772
+ // 1: no need to adjust polymorphicPaths, since we don't cross polymorphic boundary
1773
+ // const targetPolymorphicPaths = deepest.polymorphicPaths;
1774
+ // if (!targetPolymorphicPaths.has([...step.polymorphicPaths][0])) {
1775
+ // throw new Error(
1776
+ // `GrafastInternalError<53907e56-940a-4173-979d-bc620e4f1ff8>: polymorphic assumption doesn't hold. Mine = ${[
1777
+ // ...step.polymorphicPaths,
1778
+ // ]}; theirs = ${[...deepest.polymorphicPaths]}`,
1779
+ // );
1780
+ // }
1781
+ // 2: move it to target layer
1782
+ this.stepTracker.moveStepToLayerPlan(step, deepest);
1783
+ return step;
1784
+ }
1785
+ _deduplicateInnerLogic(step) {
1786
+ if (step.hasSideEffects) {
1787
+ // Never deduplicate plans with side effects.
1788
+ return null;
1789
+ }
1790
+ if (step instanceof index_js_1.__ItemStep) {
1791
+ // __ItemStep cannot be deduplicated
1792
+ return null;
1793
+ }
1794
+ // OPTIMIZE: Past Benjie thought we might want to revisit this under the
1795
+ // new Grafast system. No idea what he had in mind there though...
1796
+ if (step._stepOptions.stream) {
1797
+ // Never deduplicate streaming plans, we cannot reference the stream more
1798
+ // than once (and we aim to not cache the stream because we want its
1799
+ // entries to be garbage collected).
1800
+ return null;
1801
+ }
1802
+ const peers = this.getPeers(step);
1803
+ // Even if there is just one peer, we must still deduplicate because
1804
+ // `deduplicate` is called to indicate that the field is done being
1805
+ // planned, which the step class may want to acknowledge by locking certain
1806
+ // facets of its functionality (such as adding filters). We'll simplify its
1807
+ // work though by giving it an empty array to filter.
1808
+ const equivalentSteps = step.deduplicate(peers);
1809
+ if (equivalentSteps.length === 0) {
1810
+ // No other equivalents
1811
+ return null;
1812
+ }
1813
+ if (isDev) {
1814
+ if (!equivalentSteps.every((replacementStep) => peers.includes(replacementStep))) {
1815
+ throw new Error(`deduplicatePlan error: '${step}.deduplicate' may only return steps from its peers peers (peers = ${peers}), but it returned ${equivalentSteps}. This is currently forbidden because it could cause confusion during the optimization process, instead apply this change in 'optimize', or make sure that any child selections aren't applied until the optimize/finalize phase so that no mapping is required during deduplicate.`);
1816
+ }
1817
+ }
1818
+ // Prefer the step that's closest to the root LayerPlan; failing that, prefer the step with the lowest id.
1819
+ let minDepth = step.layerPlan.depth;
1820
+ let stepsAtMinDepth = [step];
1821
+ for (const step of equivalentSteps) {
1822
+ const depth = step.layerPlan.depth;
1823
+ if (depth < minDepth) {
1824
+ minDepth = depth;
1825
+ stepsAtMinDepth = [step];
1826
+ }
1827
+ else if (depth === minDepth) {
1828
+ stepsAtMinDepth.push(step);
1829
+ }
1830
+ }
1831
+ stepsAtMinDepth.sort((a, z) => a.id - z.id);
1832
+ return { stepsAtMinDepth, equivalentSteps };
1833
+ }
1834
+ deduplicateStep(step) {
1835
+ step.isArgumentsFinalized = true;
1836
+ if (step.deduplicate == null)
1837
+ return step;
1838
+ const result = this._deduplicateInnerLogic(step);
1839
+ if (result == null) {
1840
+ return step;
1841
+ }
1842
+ const { stepsAtMinDepth, equivalentSteps } = result;
1843
+ // Hooray, one winning layer! Find the first one by id.
1844
+ const winner = stepsAtMinDepth[0];
1845
+ if (winner.polymorphicPaths !== null) {
1846
+ const polymorphicPaths = new Set();
1847
+ for (const s of stepsAtMinDepth) {
1848
+ for (const p of s.polymorphicPaths) {
1849
+ polymorphicPaths.add(p);
1850
+ }
1851
+ }
1852
+ winner.polymorphicPaths = polymorphicPaths;
1853
+ }
1854
+ // Give the steps a chance to pass their responsibilities to the winner.
1855
+ if (winner !== step) {
1856
+ step.deduplicatedWith?.(winner);
1857
+ this.stepTracker.replaceStep(step, winner);
1858
+ }
1859
+ for (const target of equivalentSteps) {
1860
+ if (winner !== target) {
1861
+ target.deduplicatedWith?.(winner);
1862
+ this.stepTracker.replaceStep(target, winner);
1863
+ }
1864
+ }
1865
+ return winner;
1866
+ }
1867
+ /*
1868
+ * PERF: calculation if peers for deduplicate is too expensive, and shouldn't
1869
+ * be done in an expensive loop. Alternative:
1870
+ *
1871
+ * 1. build a `list` of the steps to deduplicate
1872
+ * 2. sort this `list` into "dependencies first" order
1873
+ * 3. for each step in this `list`:
1874
+ * 1. calculate the list of "compatible" LayerPlans for this step (using
1875
+ * cache of results of this step where possible)
1876
+ * 2. find all the "peer" steps within these LayerPlans
1877
+ * 3. calculate the equivalents for this step
1878
+ * 4. pick the winning equivalent, replace all other instances with this
1879
+ * 5. remove the replaced (and winning) steps from the `list`
1880
+ *
1881
+ * "compatible" layer plans are calculated by walking up the layer plan tree,
1882
+ * however:
1883
+ * - do not pass the LayerPlan of one of the dependencies
1884
+ * - do not pass a "deferred" layer plan
1885
+ *
1886
+ * "Pick the winning equivalent" is challenging, here's current thought:
1887
+ *
1888
+ * 1. find the shallowest LayerPlans (logic already written above)
1889
+ * 2. if just one shallowest LayerPlan, winner is lowest-id plan from this LayerPlan
1890
+ * 3. otherwise, if all shallowest LayerPlans are polymorphic... ARGH!
1891
+ *
1892
+ * ARGH! WHAT DOES THIS MEAN FOR BRANCHING/NON-BRANCHING PLANS. We can
1893
+ * guarantee that the dependencies must be at least a layer up (otherwise how
1894
+ * can these be peers with the same dependencies?). For non-branching
1895
+ * non-deferred plans, push it a layer up.
1896
+ */
1897
+ deduplicateStepsProcess(processed, start, step) {
1898
+ processed.add(step);
1899
+ for (const dep of step.dependencies) {
1900
+ if (dep.id >= start && !processed.has(dep)) {
1901
+ this.deduplicateStepsProcess(processed, start, dep);
1902
+ }
1903
+ }
1904
+ (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, this.deduplicateStep, this, step);
1905
+ }
1906
+ /**
1907
+ * Gives us a chance to replace nearly-duplicate plans with other existing
1908
+ * plans (and adding the necessary transforms); this means that by the time
1909
+ * we come to optimize the plan tree should already be simpler. For example
1910
+ * if you have two plans at the same level that both request row data from
1911
+ * the same database table with the same identifiers, `WHERE`, `LIMIT`,
1912
+ * `OFFSET` and `ORDER BY`, but different `SELECT`s we could merge the two
1913
+ * plans together by replacing the latter with the former and having the
1914
+ * former SELECT additional fields, then transform the results back to what
1915
+ * our child plans would be expecting.
1916
+ */
1917
+ deduplicateSteps(depth = 0) {
1918
+ const start = this.stepTracker.nextStepIdToDeduplicate;
1919
+ const end = this.stepTracker.stepCount;
1920
+ if (end === start) {
1921
+ return;
1922
+ }
1923
+ const processed = new Set();
1924
+ for (let i = start; i < end; i++) {
1925
+ const step = this.stepTracker.stepById[i];
1926
+ if (!step || step.id !== i || processed.has(step))
1927
+ continue;
1928
+ this.deduplicateStepsProcess(processed, start, step);
1929
+ }
1930
+ this.stepTracker.nextStepIdToDeduplicate = end;
1931
+ if (this.stepTracker.stepCount > end) {
1932
+ // More steps were created during deduplicate; let's deduplicate those!
1933
+ const MAX_DEPTH = 5;
1934
+ if (depth > MAX_DEPTH) {
1935
+ const newSteps = [];
1936
+ for (let i = end, l = this.stepTracker.stepCount; i < l; i++) {
1937
+ newSteps.push(this.stepTracker.stepById[i]);
1938
+ }
1939
+ throw new Error(`Whilst deduplicating steps, more steps were created; whilst deduplicating those, even more steps were created. This happened ${MAX_DEPTH} times, which suggests this might go on infinitely. Please check the deduplicate/deduplicatedWith methods on ${newSteps
1940
+ .map((s) => String(s))
1941
+ .join(", ")}.`);
1942
+ }
1943
+ return this.deduplicateSteps(depth + 1);
1944
+ }
1945
+ }
1946
+ hoistAndDeduplicate(step) {
1947
+ this.hoistStep(step);
1948
+ // Even if step wasn't hoisted, its deps may have been so we should still
1949
+ // re-deduplicate it.
1950
+ return step.deduplicate ? this.deduplicateStep(step) : step;
1951
+ }
1952
+ hoistSteps() {
1953
+ this.processSteps("hoist", "dependencies-first", false, this.hoistAndDeduplicate);
1954
+ }
1955
+ pushDownSteps() {
1956
+ this.processSteps("pushDown", "dependents-first", false, this.pushDown);
1957
+ }
1958
+ getStepOptionsForStep(step) {
1959
+ return step._stepOptions;
1960
+ /*
1961
+ // NOTE: streams can only be merged if their parameters are compatible
1962
+ // (namely they need to have equivalent `initialCount`)
1963
+ const streamLayerPlan = step.childLayerPlans.find(
1964
+ (lp) => lp.reason.type === "stream",
1965
+ );
1966
+
1967
+ // PERF: if step isn't streamable, don't create a streamLayerPlan because
1968
+ // there's no point (and then we can leverage better optimisations).
1969
+
1970
+ return {
1971
+ stream:
1972
+ streamLayerPlan && isStreamableStep(step)
1973
+ ? // && streamLayerPlan.parentStep === step
1974
+ { initialCount: streamLayerPlan.reason.initialCount }
1975
+ : null,
1976
+ };
1977
+ */
1978
+ }
1979
+ /**
1980
+ * Calls the 'optimize' method on a plan, which may cause the plan to
1981
+ * communicate with its (deep) dependencies, and even to replace itself with
1982
+ * a different plan.
1983
+ */
1984
+ optimizeStep(inStep) {
1985
+ const step = inStep.allowMultipleOptimizations && inStep.deduplicate
1986
+ ? this.deduplicateStep(inStep)
1987
+ : inStep;
1988
+ if (!step.optimize) {
1989
+ step.isOptimized = true;
1990
+ return step;
1991
+ }
1992
+ if (step.isOptimized && !step.allowMultipleOptimizations) {
1993
+ return step;
1994
+ }
1995
+ // We know if it's streaming or not based on the LayerPlan it's contained within.
1996
+ const stepOptions = this.getStepOptionsForStep(step);
1997
+ let meta;
1998
+ if (step.optimizeMetaKey !== undefined) {
1999
+ meta = this.optimizeMeta.get(step.optimizeMetaKey);
2000
+ if (!meta) {
2001
+ meta = Object.create(null);
2002
+ this.optimizeMeta.set(step.optimizeMetaKey, meta);
2003
+ }
2004
+ }
2005
+ const replacementStep = step.optimize({
2006
+ ...stepOptions,
2007
+ meta,
2008
+ });
2009
+ if (!replacementStep) {
2010
+ throw new Error(`Bug in ${step}'s class: the 'optimize' method must return a step. Hint: did you forget 'return this;'?`);
2011
+ }
2012
+ step.isOptimized = true;
2013
+ return replacementStep;
2014
+ }
2015
+ /**
2016
+ * Note that we work through dependents first so we can make sure that we
2017
+ * know all our dependent's needs before we optimise ourself.
2018
+ */
2019
+ optimizeSteps() {
2020
+ const thirdAndFutureLoopReplacedPlans = [];
2021
+ for (let loops = 0; loops < MAX_OPTIMIZATION_LOOPS; loops++) {
2022
+ let replacedPlan = false;
2023
+ this.processSteps("optimize", "dependents-first", false, loops === 0
2024
+ ? (step) => {
2025
+ const newStep = this.optimizeStep(step);
2026
+ if (newStep !== step) {
2027
+ replacedPlan = true;
2028
+ }
2029
+ return newStep;
2030
+ }
2031
+ : (step) => {
2032
+ if (step.allowMultipleOptimizations) {
2033
+ const replacement = this.optimizeStep(step);
2034
+ if (replacement !== step) {
2035
+ replacedPlan = true;
2036
+ if (loops >= 3) {
2037
+ thirdAndFutureLoopReplacedPlans.push(step);
2038
+ }
2039
+ }
2040
+ return replacement;
2041
+ }
2042
+ else {
2043
+ return step;
2044
+ }
2045
+ });
2046
+ if (!replacedPlan) {
2047
+ return;
2048
+ }
2049
+ }
2050
+ console.warn(`Optimize steps looped ${MAX_OPTIMIZATION_LOOPS} times, and was still replacing steps at the end - this could indicate a very complex plan, or badly behaved steps. Here are some of the steps that were replaced after the second loop: ${thirdAndFutureLoopReplacedPlans
2051
+ .slice(0, 10)
2052
+ .join(", ")}`);
2053
+ }
2054
+ /** Finalizes each step */
2055
+ finalizeSteps() {
2056
+ const initialStepCount = this.stepTracker.stepCount;
2057
+ assert.strictEqual(this.modifierSteps.length, 0, "No modifier steps expected when performing finalizeSteps");
2058
+ for (const step of this.stepTracker.activeSteps) {
2059
+ step.finalize();
2060
+ (0, step_js_1.assertFinalized)(step);
2061
+ if (isDev && this.stepTracker.stepCount !== initialStepCount) {
2062
+ throw new Error(`When calling ${step}.finalize() a new plan was created; this is forbidden!`);
2063
+ }
2064
+ }
2065
+ }
2066
+ finalizeLayerPlans() {
2067
+ /**
2068
+ * Adds the `dep` plan to the `copyPlanIds` for `layerPlan` and any
2069
+ * ancestor layers until we hit the layerPlan that `dep` is from.
2070
+ */
2071
+ const ensurePlanAvailableInLayer = (dep, layerPlan) => {
2072
+ let currentLayerPlan = layerPlan;
2073
+ while (dep.layerPlan !== currentLayerPlan) {
2074
+ if (currentLayerPlan.copyStepIds.includes(dep.id)) {
2075
+ break;
2076
+ }
2077
+ if (isDev && this.stepTracker.getStepById(dep.id) !== dep) {
2078
+ throw new Error(`GrafastInternalError<b291b110-396a-4c9c-814a-5466af3c50e8>: Plan mismatch; are we using a replaced step? Step ID: ${dep.id}; step: ${dep}; stepById: ${this.stepTracker.getStepById(dep.id)}`);
2079
+ }
2080
+ currentLayerPlan.copyStepIds.push(dep.id);
2081
+ currentLayerPlan = currentLayerPlan.parentLayerPlan;
2082
+ if (!currentLayerPlan) {
2083
+ throw new Error(`GrafastInternalError<8c1640b9-fa3c-440d-99e5-7693d0d7e5d1>: could not find layer plan for '${dep}' in chain from layer plan ${layerPlan}`);
2084
+ }
2085
+ }
2086
+ };
2087
+ for (const layerPlan of this.stepTracker.layerPlans) {
2088
+ if (!layerPlan) {
2089
+ continue;
2090
+ }
2091
+ layerPlan.steps = [];
2092
+ }
2093
+ for (const step of this.stepTracker.activeSteps) {
2094
+ step.layerPlan.steps.push(step);
2095
+ }
2096
+ for (const layerPlan of this.stepTracker.layerPlans) {
2097
+ if (!layerPlan) {
2098
+ continue;
2099
+ }
2100
+ layerPlan.pendingSteps = layerPlan.steps.filter((s) => !(step_js_1.$$noExec in s));
2101
+ const sideEffectSteps = layerPlan.pendingSteps.filter((s) => s.hasSideEffects);
2102
+ const pending = new Set(layerPlan.pendingSteps);
2103
+ const processed = new Set();
2104
+ const processSideEffectPlan = (step) => {
2105
+ if (processed.has(step) || isPrepopulatedStep(step)) {
2106
+ return;
2107
+ }
2108
+ processed.add(step);
2109
+ pending.delete(step);
2110
+ const sideEffectDeps = [];
2111
+ const rest = [];
2112
+ for (const dep of step.dependencies) {
2113
+ if (dep.layerPlan !== layerPlan) {
2114
+ continue;
2115
+ }
2116
+ if (processed.has(dep)) {
2117
+ continue;
2118
+ }
2119
+ if (dep.hasSideEffects) {
2120
+ sideEffectDeps.push(dep);
2121
+ }
2122
+ else {
2123
+ rest.push(dep);
2124
+ }
2125
+ }
2126
+ // Call any side effects we're dependent on
2127
+ for (const sideEffectDep of sideEffectDeps) {
2128
+ processSideEffectPlan(sideEffectDep);
2129
+ }
2130
+ // PERF: this is silly, we should be able to group them together and
2131
+ // run them in parallel, and they don't even have side effects!
2132
+ for (const dep of rest) {
2133
+ processSideEffectPlan(dep);
2134
+ }
2135
+ const phase = /*#__INLINE__*/ newLayerPlanPhase();
2136
+ phase.checkTimeout = true;
2137
+ phase.normalSteps = [{ step }];
2138
+ phase._allSteps.push(step);
2139
+ layerPlan.phases.push(phase);
2140
+ };
2141
+ for (const sideEffectStep of sideEffectSteps) {
2142
+ processSideEffectPlan(sideEffectStep);
2143
+ }
2144
+ const readyToExecute = (step) => {
2145
+ for (const dep of step.dependencies) {
2146
+ if (dep.layerPlan === layerPlan && pending.has(dep)) {
2147
+ return false;
2148
+ }
2149
+ }
2150
+ return true;
2151
+ };
2152
+ while (pending.size > 0) {
2153
+ const nextSteps = [];
2154
+ for (const step of pending) {
2155
+ if (readyToExecute(step)) {
2156
+ nextSteps.push(step);
2157
+ }
2158
+ }
2159
+ if (nextSteps.length === 0) {
2160
+ throw new Error(`GrafastInternalError<2904ebbf-6344-4f2b-9305-8db9c1ff29c5>: Could not compute execution order?! Remaining: ${[
2161
+ ...pending,
2162
+ ]} (processed: ${[...processed]}; all: ${layerPlan.pendingSteps})`);
2163
+ }
2164
+ // Do not add to processed until whole layer is known
2165
+ const phase = /*#__INLINE__*/ newLayerPlanPhase();
2166
+ for (const step of nextSteps) {
2167
+ processed.add(step);
2168
+ pending.delete(step);
2169
+ if (step.isSyncAndSafe && (0, step_js_1.isUnbatchedExecutableStep)(step)) {
2170
+ if (phase.unbatchedSyncAndSafeSteps !== undefined) {
2171
+ phase.unbatchedSyncAndSafeSteps.push({
2172
+ step,
2173
+ scratchpad: undefined,
2174
+ });
2175
+ }
2176
+ else {
2177
+ phase.unbatchedSyncAndSafeSteps = [
2178
+ { step, scratchpad: undefined },
2179
+ ];
2180
+ }
2181
+ }
2182
+ else {
2183
+ if (!step.isSyncAndSafe || step.hasSideEffects) {
2184
+ phase.checkTimeout = true;
2185
+ }
2186
+ if (phase.normalSteps !== undefined) {
2187
+ phase.normalSteps.push({ step });
2188
+ }
2189
+ else {
2190
+ phase.normalSteps = [{ step }];
2191
+ }
2192
+ }
2193
+ }
2194
+ // Add more isSyncAndSafe unbatched steps if possible
2195
+ let foundOne = false;
2196
+ do {
2197
+ foundOne = false;
2198
+ for (const step of pending) {
2199
+ if (step.isSyncAndSafe && (0, step_js_1.isUnbatchedExecutableStep)(step)) {
2200
+ if (readyToExecute(step)) {
2201
+ processed.add(step);
2202
+ pending.delete(step);
2203
+ foundOne = true;
2204
+ if (phase.unbatchedSyncAndSafeSteps !== undefined) {
2205
+ phase.unbatchedSyncAndSafeSteps.push({
2206
+ step,
2207
+ scratchpad: undefined,
2208
+ });
2209
+ }
2210
+ else {
2211
+ phase.unbatchedSyncAndSafeSteps = [
2212
+ { step, scratchpad: undefined },
2213
+ ];
2214
+ }
2215
+ }
2216
+ }
2217
+ }
2218
+ } while (foundOne);
2219
+ if (phase.normalSteps !== undefined) {
2220
+ for (const { step } of phase.normalSteps) {
2221
+ phase._allSteps.push(step);
2222
+ }
2223
+ }
2224
+ if (phase.unbatchedSyncAndSafeSteps !== undefined) {
2225
+ for (const { step } of phase.unbatchedSyncAndSafeSteps) {
2226
+ phase._allSteps.push(step);
2227
+ }
2228
+ }
2229
+ layerPlan.phases.push(phase);
2230
+ }
2231
+ // PERF: this could probably be faster.
2232
+ // Populate copyPlanIds for each step
2233
+ for (const step of layerPlan.steps) {
2234
+ // Items shouldn't have their "list" copied in.
2235
+ if (step_js_1.$$noExec in step) {
2236
+ continue;
2237
+ }
2238
+ for (const dep of step.dependencies) {
2239
+ ensurePlanAvailableInLayer(dep, layerPlan);
2240
+ }
2241
+ }
2242
+ const $root = layerPlan.rootStep;
2243
+ if ($root) {
2244
+ ensurePlanAvailableInLayer($root, layerPlan);
2245
+ }
2246
+ // Copy polymorphic parentStepId
2247
+ if (layerPlan.reason.type === "polymorphic") {
2248
+ const parentStep = layerPlan.reason.parentStep;
2249
+ ensurePlanAvailableInLayer(parentStep, layerPlan);
2250
+ }
2251
+ // Ensure list is accessible in parent layerPlan
2252
+ if (layerPlan.reason.type === "listItem") {
2253
+ const parentStep = layerPlan.reason.parentStep;
2254
+ ensurePlanAvailableInLayer(parentStep, layerPlan.parentLayerPlan);
2255
+ }
2256
+ }
2257
+ // Populate copyPlanIds for output plans' rootStepId
2258
+ this.stepTracker.allOutputPlans.forEach((outputPlan) => {
2259
+ ensurePlanAvailableInLayer(outputPlan.rootStep, outputPlan.layerPlan);
2260
+ });
2261
+ for (const layerPlan of this.stepTracker.layerPlans) {
2262
+ if (layerPlan !== null) {
2263
+ layerPlan.finalize();
2264
+ }
2265
+ }
2266
+ }
2267
+ /** Optimizes each output plan */
2268
+ optimizeOutputPlans() {
2269
+ this.stepTracker.allOutputPlans.forEach((outputPlan) => outputPlan.optimize());
2270
+ }
2271
+ /** Finalizes each output plan */
2272
+ finalizeOutputPlans() {
2273
+ this.stepTracker.allOutputPlans.forEach((outputPlan) => outputPlan.finalize());
2274
+ }
2275
+ walkOutputPlans(outputPlan, callback) {
2276
+ callback(outputPlan);
2277
+ if (outputPlan.child !== null) {
2278
+ this.walkOutputPlans(outputPlan.child, callback);
2279
+ }
2280
+ if (outputPlan.childByTypeName !== undefined) {
2281
+ Object.values(outputPlan.childByTypeName).forEach((childOutputPlan) => {
2282
+ this.walkOutputPlans(childOutputPlan, callback);
2283
+ });
2284
+ }
2285
+ for (const spec of Object.values(outputPlan.keys)) {
2286
+ if (spec.type === "outputPlan") {
2287
+ this.walkOutputPlans(spec.outputPlan, callback);
2288
+ }
2289
+ }
2290
+ for (const defer of outputPlan.deferredOutputPlans) {
2291
+ this.walkOutputPlans(defer, callback);
2292
+ }
2293
+ }
2294
+ generatePlanJSON() {
2295
+ function printStep(step) {
2296
+ const metaString = step.toStringMeta();
2297
+ return {
2298
+ id: step.id,
2299
+ stepClass: step.constructor.name,
2300
+ metaString: metaString ? (0, index_js_1.stripAnsi)(metaString) : metaString,
2301
+ bucketId: step.layerPlan.id,
2302
+ dependencyIds: step.dependencies.map((d) => d.id),
2303
+ polymorphicPaths: step.polymorphicPaths
2304
+ ? [...step.polymorphicPaths]
2305
+ : undefined,
2306
+ isSyncAndSafe: step.isSyncAndSafe || undefined,
2307
+ supportsUnbatched: typeof step.unbatchedExecute === "function" || undefined,
2308
+ hasSideEffects: step.hasSideEffects || undefined,
2309
+ extra: step.planJSONExtra(),
2310
+ };
2311
+ }
2312
+ function printPhase(phase) {
2313
+ return {
2314
+ normalStepIds: phase.normalSteps?.map((s) => s.step.id),
2315
+ unbatchedStepIds: phase.unbatchedSyncAndSafeSteps?.map((s) => s.step.id),
2316
+ };
2317
+ }
2318
+ function printBucketReason(reason) {
2319
+ switch (reason.type) {
2320
+ case "root": {
2321
+ const { type } = reason;
2322
+ return { type };
2323
+ }
2324
+ case "nullableBoundary": {
2325
+ const { type, parentStep } = reason;
2326
+ return { type, parentStepId: parentStep.id };
2327
+ }
2328
+ case "listItem": {
2329
+ const { type, parentStep, stream } = reason;
2330
+ return { type, parentStepId: parentStep.id, stream };
2331
+ }
2332
+ case "subscription": {
2333
+ const { type } = reason;
2334
+ return { type };
2335
+ }
2336
+ case "mutationField": {
2337
+ const { type, mutationIndex } = reason;
2338
+ return { type, mutationIndex };
2339
+ }
2340
+ case "defer": {
2341
+ const { type, label } = reason;
2342
+ return { type, label };
2343
+ }
2344
+ case "polymorphic": {
2345
+ const { type, typeNames, parentStep, polymorphicPaths } = reason;
2346
+ return {
2347
+ type,
2348
+ typeNames,
2349
+ parentStepId: parentStep.id,
2350
+ polymorphicPaths: [...polymorphicPaths],
2351
+ };
2352
+ }
2353
+ case "subroutine": {
2354
+ const { type, parentStep } = reason;
2355
+ return { type, parentStepId: parentStep.id };
2356
+ }
2357
+ default: {
2358
+ const never = reason;
2359
+ throw new Error(`Failed to process layer plan reason ${(0, inspect_js_1.inspect)(never)}`);
2360
+ }
2361
+ }
2362
+ }
2363
+ function printBucket(lp) {
2364
+ lp.reason;
2365
+ return {
2366
+ id: lp.id,
2367
+ reason: printBucketReason(lp.reason),
2368
+ copyStepIds: lp.copyStepIds,
2369
+ phases: lp.phases.map(printPhase),
2370
+ steps: lp.steps.map(printStep),
2371
+ children: lp.children.map(printBucket),
2372
+ rootStepId: lp.rootStep ? lp.rootStep.id : null,
2373
+ };
2374
+ }
2375
+ return {
2376
+ version: "v1",
2377
+ rootBucket: printBucket(this.rootLayerPlan),
2378
+ };
2379
+ }
2380
+ finishSubroutine(subroutineStep, layerPlan) {
2381
+ // Now find anything that these plans are dependent on and make ourself
2382
+ // dependent on them.
2383
+ const process = (lp, known) => {
2384
+ for (const step of this.stepTracker.activeSteps) {
2385
+ if (step.layerPlan === lp) {
2386
+ for (const dep of step.dependencies) {
2387
+ if (!known.includes(dep.layerPlan)) {
2388
+ // Naughty naughty
2389
+ subroutineStep.addDependency(dep);
2390
+ }
2391
+ }
2392
+ }
2393
+ }
2394
+ for (const child of lp.children) {
2395
+ process(child, [...known, child]);
2396
+ }
2397
+ };
2398
+ process(layerPlan, [layerPlan]);
2399
+ }
2400
+ /**
2401
+ * HIGHLY EXPERIMENTAL!
2402
+ *
2403
+ * @internal
2404
+ */
2405
+ deleteLayerPlan(layerPlan) {
2406
+ this.stepTracker.deleteLayerPlan(layerPlan);
2407
+ }
2408
+ getStepsByMetaKey(metaKey) {
2409
+ const matches = [];
2410
+ for (const step of this.stepTracker.activeSteps) {
2411
+ if (step.metaKey === metaKey) {
2412
+ matches.push(step);
2413
+ }
2414
+ }
2415
+ return matches;
2416
+ }
2417
+ getStepsByStepClass(klass) {
2418
+ const matches = [];
2419
+ for (const step of this.stepTracker.activeSteps) {
2420
+ if (step instanceof klass) {
2421
+ matches.push(step);
2422
+ }
2423
+ }
2424
+ return matches;
2425
+ }
2426
+ }
2427
+ exports.OperationPlan = OperationPlan;
2428
+ function makeDefaultPlan(fieldName) {
2429
+ return ($step) => typeof $step.get === "function"
2430
+ ? $step.get(fieldName)
2431
+ : (0, access_js_1.access)($step, [fieldName]);
2432
+ }
2433
+ function makeMetaByMetaKeysFactory(allMetaKeysList) {
2434
+ const l = allMetaKeysList.length;
2435
+ // Optimize the common cases
2436
+ if (l === 0) {
2437
+ return makeMetaByMetaKeys0;
2438
+ }
2439
+ else if (l === 1) {
2440
+ return makeMetaByMetaKeys1Factory(allMetaKeysList[0]);
2441
+ }
2442
+ else if (l === 2) {
2443
+ return makeMetaByMetaKeys2Factory(allMetaKeysList[0], allMetaKeysList[1]);
2444
+ }
2445
+ else if (l === 3) {
2446
+ return makeMetaByMetaKeys3Factory(allMetaKeysList[0], allMetaKeysList[1], allMetaKeysList[2]);
2447
+ }
2448
+ else if (l === 4) {
2449
+ return makeMetaByMetaKeys4Factory(allMetaKeysList[0], allMetaKeysList[1], allMetaKeysList[2], allMetaKeysList[3]);
2450
+ }
2451
+ else if (l === 5) {
2452
+ return makeMetaByMetaKeys5Factory(allMetaKeysList[0], allMetaKeysList[1], allMetaKeysList[2], allMetaKeysList[3], allMetaKeysList[4]);
2453
+ }
2454
+ else if (l === 6) {
2455
+ return makeMetaByMetaKeys6Factory(allMetaKeysList[0], allMetaKeysList[1], allMetaKeysList[2], allMetaKeysList[3], allMetaKeysList[4], allMetaKeysList[5]);
2456
+ }
2457
+ return function makeMetaByMetaKey() {
2458
+ const metaByMetaKey = Object.create(null);
2459
+ for (let i = 0; i < l; i++) {
2460
+ metaByMetaKey[allMetaKeysList[i]] = Object.create(null);
2461
+ }
2462
+ return metaByMetaKey;
2463
+ };
2464
+ }
2465
+ const EMPTY_OBJECT = Object.freeze(Object.create(null));
2466
+ function makeMetaByMetaKeys0() {
2467
+ return EMPTY_OBJECT;
2468
+ }
2469
+ function makeMetaByMetaKeys1Factory(key1) {
2470
+ return function makeMetaByMetaKeys1() {
2471
+ const obj = Object.create(null);
2472
+ obj[key1] = Object.create(null);
2473
+ return obj;
2474
+ };
2475
+ }
2476
+ function makeMetaByMetaKeys2Factory(key1, key2) {
2477
+ return function makeMetaByMetaKeys2() {
2478
+ const obj = Object.create(null);
2479
+ obj[key1] = Object.create(null);
2480
+ obj[key2] = Object.create(null);
2481
+ return obj;
2482
+ };
2483
+ }
2484
+ function makeMetaByMetaKeys3Factory(key1, key2, key3) {
2485
+ return function makeMetaByMetaKeys3() {
2486
+ const obj = Object.create(null);
2487
+ obj[key1] = Object.create(null);
2488
+ obj[key2] = Object.create(null);
2489
+ obj[key3] = Object.create(null);
2490
+ return obj;
2491
+ };
2492
+ }
2493
+ function makeMetaByMetaKeys4Factory(key1, key2, key3, key4) {
2494
+ return function makeMetaByMetaKeys4() {
2495
+ const obj = Object.create(null);
2496
+ obj[key1] = Object.create(null);
2497
+ obj[key2] = Object.create(null);
2498
+ obj[key3] = Object.create(null);
2499
+ obj[key4] = Object.create(null);
2500
+ return obj;
2501
+ };
2502
+ }
2503
+ function makeMetaByMetaKeys5Factory(key1, key2, key3, key4, key5) {
2504
+ return function makeMetaByMetaKeys5() {
2505
+ const obj = Object.create(null);
2506
+ obj[key1] = Object.create(null);
2507
+ obj[key2] = Object.create(null);
2508
+ obj[key3] = Object.create(null);
2509
+ obj[key4] = Object.create(null);
2510
+ obj[key5] = Object.create(null);
2511
+ return obj;
2512
+ };
2513
+ }
2514
+ function makeMetaByMetaKeys6Factory(key1, key2, key3, key4, key5, key6) {
2515
+ return function makeMetaByMetaKeys6() {
2516
+ const obj = Object.create(null);
2517
+ obj[key1] = Object.create(null);
2518
+ obj[key2] = Object.create(null);
2519
+ obj[key3] = Object.create(null);
2520
+ obj[key4] = Object.create(null);
2521
+ obj[key5] = Object.create(null);
2522
+ obj[key6] = Object.create(null);
2523
+ return obj;
2524
+ };
2525
+ }
2526
+ function newLayerPlanPhase() {
2527
+ return {
2528
+ checkTimeout: false,
2529
+ normalSteps: undefined,
2530
+ unbatchedSyncAndSafeSteps: undefined,
2531
+ _allSteps: [],
2532
+ };
2533
+ }
2534
+ function throwNoNewStepsError(operationPlan, actionDescription, step, previousStepCount, message) {
2535
+ const newSteps = [];
2536
+ for (let i = previousStepCount, l = operationPlan.stepTracker.stepCount; i < l; i++) {
2537
+ newSteps.push(operationPlan.stepTracker.stepById[i]);
2538
+ }
2539
+ throw new Error(`${message}; whilst performing ${actionDescription} of ${step} the following new steps were created: ${newSteps
2540
+ .map((s) => String(s))
2541
+ .join(", ")}`);
2542
+ }
2543
+ //# sourceMappingURL=OperationPlan.js.map