grafast 0.1.1-beta.21 → 0.1.1-beta.23

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 (94) hide show
  1. package/README.md +0 -4
  2. package/dist/args.js +3 -3
  3. package/dist/constants.d.ts +79 -0
  4. package/dist/constants.js +79 -0
  5. package/dist/engine/LayerPlan.d.ts +71 -8
  6. package/dist/engine/LayerPlan.js +375 -265
  7. package/dist/engine/OperationPlan.d.ts +77 -14
  8. package/dist/engine/OperationPlan.js +1508 -382
  9. package/dist/engine/OperationPlanTypes.d.ts +66 -0
  10. package/dist/engine/OperationPlanTypes.js +3 -0
  11. package/dist/engine/OutputPlan.d.ts +6 -0
  12. package/dist/engine/OutputPlan.js +138 -82
  13. package/dist/engine/StepTracker.js +76 -17
  14. package/dist/engine/executeBucket.js +327 -166
  15. package/dist/engine/lib/defaultPlanResolver.d.ts +1 -4
  16. package/dist/engine/lib/defaultPlanResolver.js +1 -5
  17. package/dist/engine/lib/withGlobalLayerPlan.d.ts +3 -1
  18. package/dist/engine/lib/withGlobalLayerPlan.js +19 -1
  19. package/dist/envelop.js +14 -3
  20. package/dist/error.d.ts +1 -1
  21. package/dist/error.js +9 -9
  22. package/dist/establishOperationPlan.d.ts +2 -1
  23. package/dist/establishOperationPlan.js +10 -10
  24. package/dist/execute.js +7 -5
  25. package/dist/global.d.ts +10 -0
  26. package/dist/global.js +25 -0
  27. package/dist/grafastGraphql.js +4 -3
  28. package/dist/grafastPrint.js +36 -4
  29. package/dist/index.d.ts +76 -8
  30. package/dist/index.js +37 -33
  31. package/dist/input.js +20 -1
  32. package/dist/inspect.js +1 -0
  33. package/dist/interfaces.d.ts +80 -122
  34. package/dist/interfaces.js +0 -77
  35. package/dist/makeGrafastSchema.d.ts +118 -35
  36. package/dist/makeGrafastSchema.js +197 -29
  37. package/dist/mermaid.js +198 -97
  38. package/dist/operationPlan-input.d.ts +1 -1
  39. package/dist/operationPlan-input.js +15 -5
  40. package/dist/planJSONInterfaces.d.ts +24 -5
  41. package/dist/prepare.d.ts +16 -9
  42. package/dist/prepare.js +54 -51
  43. package/dist/step.d.ts +45 -9
  44. package/dist/step.js +139 -22
  45. package/dist/steps/__flag.d.ts +3 -2
  46. package/dist/steps/__flag.js +31 -29
  47. package/dist/steps/__inputObject.d.ts +1 -1
  48. package/dist/steps/__inputStaticLeaf.d.ts +3 -2
  49. package/dist/steps/__inputStaticLeaf.js +13 -7
  50. package/dist/steps/__item.d.ts +1 -1
  51. package/dist/steps/__item.js +3 -3
  52. package/dist/steps/__trackedValue.d.ts +6 -3
  53. package/dist/steps/__trackedValue.js +25 -9
  54. package/dist/steps/__value.d.ts +2 -1
  55. package/dist/steps/__value.js +5 -0
  56. package/dist/steps/access.d.ts +17 -1
  57. package/dist/steps/access.js +14 -1
  58. package/dist/steps/applyInput.d.ts +1 -1
  59. package/dist/steps/applyInput.js +7 -6
  60. package/dist/steps/applyTransforms.js +9 -7
  61. package/dist/steps/bakedInput.js +2 -2
  62. package/dist/steps/coalesce.d.ts +15 -0
  63. package/dist/steps/coalesce.js +36 -0
  64. package/dist/steps/constant.js +16 -14
  65. package/dist/steps/each.d.ts +2 -2
  66. package/dist/steps/first.d.ts +2 -0
  67. package/dist/steps/first.js +5 -1
  68. package/dist/steps/graphqlResolver.d.ts +2 -2
  69. package/dist/steps/graphqlResolver.js +63 -161
  70. package/dist/steps/index.d.ts +3 -15
  71. package/dist/steps/index.js +6 -41
  72. package/dist/steps/lambda.js +1 -1
  73. package/dist/steps/last.d.ts +2 -0
  74. package/dist/steps/last.js +4 -0
  75. package/dist/steps/listTransform.d.ts +2 -2
  76. package/dist/steps/listTransform.js +11 -13
  77. package/dist/steps/load.d.ts +28 -22
  78. package/dist/steps/load.js +47 -1
  79. package/dist/steps/node.d.ts +10 -7
  80. package/dist/steps/node.js +5 -19
  81. package/dist/steps/object.js +6 -1
  82. package/dist/steps/proxy.d.ts +0 -6
  83. package/dist/steps/proxy.js +3 -3
  84. package/dist/steps/remapKeys.d.ts +1 -1
  85. package/dist/steps/remapKeys.js +0 -1
  86. package/dist/utils.d.ts +27 -5
  87. package/dist/utils.js +235 -15
  88. package/dist/version.d.ts +1 -1
  89. package/dist/version.js +1 -1
  90. package/package.json +7 -7
  91. package/dist/polymorphic.d.ts +0 -14
  92. package/dist/polymorphic.js +0 -72
  93. package/dist/steps/polymorphicBranch.d.ts +0 -27
  94. package/dist/steps/polymorphicBranch.js +0 -62
@@ -6,12 +6,12 @@ const lru_1 = tslib_1.__importDefault(require("@graphile/lru"));
6
6
  const graphql = tslib_1.__importStar(require("graphql"));
7
7
  const tamedevil_1 = tslib_1.__importDefault(require("tamedevil"));
8
8
  const assert = tslib_1.__importStar(require("../assert.js"));
9
+ const constants_js_1 = require("../constants.js");
9
10
  const graphqlCollectFields_js_1 = require("../graphqlCollectFields.js");
10
11
  const graphqlMergeSelectionSets_js_1 = require("../graphqlMergeSelectionSets.js");
11
12
  const index_js_1 = require("../index.js");
12
13
  const input_js_1 = require("../input.js");
13
14
  const inspect_js_1 = require("../inspect.js");
14
- const interfaces_js_1 = require("../interfaces.js");
15
15
  const operationPlan_input_js_1 = require("../operationPlan-input.js");
16
16
  const step_js_1 = require("../step.js");
17
17
  const connection_js_1 = require("../steps/connection.js");
@@ -28,6 +28,7 @@ const StepTracker_js_1 = require("./StepTracker.js");
28
28
  const atpe = typeof process !== "undefined" && process.env.ALWAYS_THROW_PLANNING_ERRORS;
29
29
  const ALWAYS_THROW_PLANNING_ERRORS = atpe === "1";
30
30
  const THROW_PLANNING_ERRORS_ON_SIDE_EFFECTS = atpe === "2";
31
+ const DEFAULT_MAX_DEPTH = 100;
31
32
  /**
32
33
  * Returns true for steps that the system populates automatically without executing.
33
34
  */
@@ -48,6 +49,7 @@ const { assertObjectType, defaultFieldResolver, getNamedType, getNullableType, i
48
49
  */
49
50
  let planningTimeoutWarmupMultiplier = 5;
50
51
  const EMPTY_ARRAY = Object.freeze([]);
52
+ const EMPTY_SET = new Set();
51
53
  exports.POLYMORPHIC_ROOT_PATH = null;
52
54
  exports.POLYMORPHIC_ROOT_PATHS = null;
53
55
  Object.freeze(exports.POLYMORPHIC_ROOT_PATHS);
@@ -57,20 +59,20 @@ const REASON_ROOT = Object.freeze({ type: "root" });
57
59
  const OUTPUT_PLAN_TYPE_NULL = Object.freeze({ mode: "null" });
58
60
  const OUTPUT_PLAN_TYPE_ARRAY = Object.freeze({ mode: "array" });
59
61
  const newValueStepCallback = (isImmutable) => new index_js_1.__ValueStep(isImmutable);
60
- const NO_ARGS = {
61
- get() {
62
- throw new Error(`This field doesn't have any arguments`);
63
- },
64
- };
62
+ const NO_ARGS = Object.create(null);
65
63
  class OperationPlan {
66
- constructor(schema, operation, fragments, variableValuesConstraints, variableValues, contextConstraints, context, rootValueConstraints, rootValue, planningTimeout) {
64
+ constructor(schema, operation, fragments, variableValuesConstraints, variableValues, contextConstraints, context, rootValueConstraints, rootValue, options) {
67
65
  this.schema = schema;
68
66
  this.operation = operation;
69
67
  this.fragments = fragments;
70
68
  this.variableValues = variableValues;
71
69
  this.context = context;
72
70
  this.rootValue = rootValue;
73
- this.planningTimeout = planningTimeout;
71
+ /**
72
+ * For use by `inputStep()` only.
73
+ * @internal
74
+ */
75
+ this._inputStepCache = new Map();
74
76
  /**
75
77
  * What state is the OpPlan in?
76
78
  *
@@ -95,7 +97,7 @@ class OperationPlan {
95
97
  /**
96
98
  * @internal
97
99
  */
98
- this.itemStepIdByListStepId = Object.create(null);
100
+ this.itemStepIdByListStepIdByParentLayerPlanId = Object.create(null);
99
101
  /**
100
102
  * If true, then this operation doesn't use (custom) resolvers.
101
103
  */
@@ -106,6 +108,7 @@ class OperationPlan {
106
108
  this.optimizeMeta = new Map();
107
109
  /** @internal */
108
110
  this.valueNodeToStaticValueCache = new Map();
111
+ this.frozenPlanningPaths = index_js_1.isDev ? null : new Set();
109
112
  /** @internal Use plan.getStep(id) instead. */
110
113
  this.getStep = index_js_1.isDev
111
114
  ? (id, requestingStep) => {
@@ -122,14 +125,28 @@ class OperationPlan {
122
125
  if (step == null) {
123
126
  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.`);
124
127
  }
125
- return step[interfaces_js_1.$$proxy] ?? step;
128
+ return step[constants_js_1.$$proxy] ?? step;
126
129
  }
127
130
  : (id, _requestingStep) => {
128
131
  const step = this.stepTracker.getStepById(id);
129
- return step[interfaces_js_1.$$proxy] ?? step;
132
+ return step[constants_js_1.$$proxy] ?? step;
130
133
  };
131
- this.polymorphicLayerPlanByPathByLayerPlan = new Map();
132
- this._cacheStepStoreByLayerPlanAndActionKey = Object.create(null);
134
+ // This is the queue of things to be done _IN ORDER_
135
+ this.planningQueue = [];
136
+ // This is the queue of things to be done grouped by their planning path.
137
+ // The result of this is not necessarily in order, but it does save us
138
+ // time and effort in `planPending()`
139
+ this.planningQueueByPlanningPath = new Map();
140
+ this.planFieldBatch = null;
141
+ this._cacheStepStoreByActionKeyByLayerPlan = new Map();
142
+ this._immutableCacheStepStoreAndActionKey = Object.create(null);
143
+ this.planningTimeout = options?.timeouts?.planning ?? null;
144
+ this.maxPlanningDepth = options?.maxPlanningDepth ?? DEFAULT_MAX_DEPTH;
145
+ this.resolveInfoOperationBase = {
146
+ schema,
147
+ operation,
148
+ fragments,
149
+ };
133
150
  this.variableValuesConstraints = variableValuesConstraints;
134
151
  this.contextConstraints = contextConstraints;
135
152
  this.rootValueConstraints = rootValueConstraints;
@@ -165,7 +182,7 @@ class OperationPlan {
165
182
  }
166
183
  this.operationType = operation.operation;
167
184
  this.phase = "plan";
168
- this.rootLayerPlan = new LayerPlan_js_1.LayerPlan(this, null, REASON_ROOT);
185
+ this.rootLayerPlan = new LayerPlan_js_1.LayerPlan(this, REASON_ROOT);
169
186
  // Set up the shared steps for variables, context and rootValue
170
187
  [this.variableValuesStep, this.trackedVariableValuesStep] = this.track(variableValues, this.variableValuesConstraints, this.operation.variableDefinitions);
171
188
  [this.contextStep, this.trackedContextStep] = this.track(context, this.contextConstraints);
@@ -293,8 +310,8 @@ class OperationPlan {
293
310
  const elapsed = now - this.startTime;
294
311
  if (elapsed > this.planningTimeout * planningTimeoutWarmupMultiplier) {
295
312
  throw new index_js_1.SafeError("Operation took too long to plan and was aborted. Please simplify the request and try again.", {
296
- [interfaces_js_1.$$timeout]: this.planningTimeout,
297
- [interfaces_js_1.$$ts]: now,
313
+ [constants_js_1.$$timeout]: this.planningTimeout,
314
+ [constants_js_1.$$ts]: now,
298
315
  });
299
316
  }
300
317
  }
@@ -380,7 +397,18 @@ class OperationPlan {
380
397
  typeName: this.queryType.name,
381
398
  }, locationDetails);
382
399
  this.rootOutputPlan = outputPlan;
383
- this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, this.trackedRootValueStep, rootType, this.operation.selectionSet.selections, true);
400
+ this.queueNextLayer(this.planSelectionSet, {
401
+ outputPlan,
402
+ path: [],
403
+ planningPath: rootType.name + ".",
404
+ polymorphicPaths: exports.POLYMORPHIC_ROOT_PATHS,
405
+ parentStep: this.trackedRootValueStep,
406
+ positionType: rootType,
407
+ layerPlan: this.rootLayerPlan,
408
+ selections: this.operation.selectionSet.selections,
409
+ resolverEmulation: true,
410
+ });
411
+ this.planPending();
384
412
  if (this.loc !== null)
385
413
  this.loc.pop();
386
414
  }
@@ -394,7 +422,6 @@ class OperationPlan {
394
422
  if (!rootType) {
395
423
  throw new index_js_1.SafeError("No mutation type found in schema");
396
424
  }
397
- this.deduplicateSteps();
398
425
  const locationDetails = {
399
426
  node: this.operation.selectionSet.selections,
400
427
  parentTypeName: null,
@@ -406,7 +433,19 @@ class OperationPlan {
406
433
  typeName: rootType.name,
407
434
  }, locationDetails);
408
435
  this.rootOutputPlan = outputPlan;
409
- this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, this.trackedRootValueStep, rootType, this.operation.selectionSet.selections, true, true);
436
+ this.queueNextLayer(this.planSelectionSet, {
437
+ outputPlan,
438
+ path: [],
439
+ planningPath: rootType.name + ".",
440
+ polymorphicPaths: exports.POLYMORPHIC_ROOT_PATHS,
441
+ parentStep: this.trackedRootValueStep,
442
+ positionType: rootType,
443
+ layerPlan: this.rootLayerPlan,
444
+ selections: this.operation.selectionSet.selections,
445
+ resolverEmulation: true,
446
+ isMutation: true,
447
+ });
448
+ this.planPending();
410
449
  if (this.loc !== null)
411
450
  this.loc.pop();
412
451
  }
@@ -420,8 +459,9 @@ class OperationPlan {
420
459
  if (!rootType) {
421
460
  throw new index_js_1.SafeError("No subscription type found in schema");
422
461
  }
462
+ const planningPath = rootType.name + ".";
423
463
  const selectionSet = this.operation.selectionSet;
424
- 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, (0, graphqlCollectFields_js_1.newSelectionSetDigest)(false));
464
+ const groupedFieldSet = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, planningPath, graphqlCollectFields_js_1.graphqlCollectFields, null, this, this.trackedRootValueStep.id, rootType, selectionSet.selections, (0, graphqlCollectFields_js_1.newSelectionSetDigest)(false));
425
465
  if (groupedFieldSet.deferred !== undefined) {
426
466
  throw new index_js_1.SafeError("@defer forbidden on subscription root selection set");
427
467
  }
@@ -452,46 +492,63 @@ class OperationPlan {
452
492
  const subscriptionPlanResolver = rawSubscriptionPlanResolver;
453
493
  const fieldArgsSpec = fieldSpec.args;
454
494
  const trackedArguments = fieldArgsSpec.length > 0
455
- ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, this.getTrackedArguments, this, fieldArgsSpec, field)
495
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, planningPath, this.getTrackedArguments, this, fieldArgsSpec, field)
456
496
  : NO_ARGS;
457
497
  if (subscriptionPlanResolver !== undefined) {
458
498
  // PERF: optimize this
459
- const { haltTree, step: subscribeStep } = this.planField(rootType.name, fieldName, this.rootLayerPlan, path, exports.POLYMORPHIC_ROOT_PATHS, subscriptionPlanResolver, "subscribePlan", this.trackedRootValueStep, fieldSpec, trackedArguments, true);
499
+ const { haltTree, step: subscribeStep } = this.batchPlanField({
500
+ typeName: rootType.name,
501
+ fieldName,
502
+ layerPlan: this.rootLayerPlan,
503
+ path,
504
+ polymorphicPaths: exports.POLYMORPHIC_ROOT_PATHS,
505
+ planningPath,
506
+ planResolver: subscriptionPlanResolver,
507
+ applyAfterMode: "subscribePlan",
508
+ rawParentStep: this.trackedRootValueStep,
509
+ field: fieldSpec,
510
+ trackedArguments,
511
+ streamDetails: true,
512
+ })();
460
513
  if (haltTree) {
461
514
  throw new index_js_1.SafeError("Failed to setup subscription");
462
515
  }
463
516
  this.rootLayerPlan.setRootStep(subscribeStep);
464
- const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, this.rootLayerPlan, {
517
+ const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, {
465
518
  type: "subscription",
519
+ parentLayerPlan: this.rootLayerPlan,
466
520
  });
467
- const $__item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => new index_js_1.__ItemStep(subscribeStep));
521
+ const $__item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, planningPath, () => new index_js_1.__ItemStep(subscribeStep));
468
522
  subscriptionEventLayerPlan.setRootStep($__item);
469
- let streamItemPlan = (0, utils_js_1.hasItemPlan)(subscribeStep)
470
- ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, subscribeStep.itemPlan, subscribeStep, $__item)
523
+ const streamItemPlan = (0, utils_js_1.hasItemPlan)(subscribeStep)
524
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, planningPath, subscribeStep.itemPlan, subscribeStep, $__item)
471
525
  : $__item;
472
- // WE MUST RE-FETCH STEPS AFTER DEDUPLICATION!
473
- this.deduplicateSteps();
474
- streamItemPlan = this.stepTracker.getStepById(streamItemPlan.id);
475
526
  const outputPlan = new OutputPlan_js_1.OutputPlan(subscriptionEventLayerPlan, this.rootValueStep, { mode: "root", typeName: rootType.name }, locationDetails);
476
527
  this.rootOutputPlan = outputPlan;
477
- this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, streamItemPlan, rootType, selectionSet.selections, false);
528
+ this.queueNextLayer(this.planSelectionSet, {
529
+ outputPlan,
530
+ path: [],
531
+ planningPath,
532
+ polymorphicPaths: exports.POLYMORPHIC_ROOT_PATHS,
533
+ parentStep: streamItemPlan,
534
+ positionType: rootType,
535
+ layerPlan: subscriptionEventLayerPlan,
536
+ selections: selectionSet.selections,
537
+ resolverEmulation: false,
538
+ });
539
+ this.planPending();
478
540
  }
479
541
  else {
480
- const subscribeStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => {
481
- const $args = (0, index_js_1.object)(field.arguments?.reduce((memo, arg) => {
482
- memo[arg.name.value] = trackedArguments.get(arg.name.value);
483
- return memo;
484
- }, Object.create(null)) ?? Object.create(null));
542
+ const subscribeStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, planningPath, () => {
543
+ const $args = (0, index_js_1.object)(trackedArguments);
485
544
  const rawResolver = fieldSpec.resolve;
486
545
  const rawSubscriber = fieldSpec.subscribe;
487
546
  return (0, graphqlResolver_js_1.graphqlResolver)(rawResolver, rawSubscriber, this.trackedRootValueStep, $args, {
547
+ ...this.resolveInfoOperationBase,
488
548
  fieldName,
489
549
  fieldNodes: fields,
490
- fragments: this.fragments,
491
- operation: this.operation,
492
550
  parentType: this.subscriptionType,
493
551
  returnType: fieldSpec.type,
494
- schema: this.schema,
495
552
  // @ts-ignore
496
553
  path: {
497
554
  typename: this.subscriptionType.name,
@@ -503,47 +560,81 @@ class OperationPlan {
503
560
  subscribeStep._stepOptions.stream = {};
504
561
  subscribeStep._stepOptions.walkIterable = true;
505
562
  this.rootLayerPlan.setRootStep(subscribeStep);
506
- const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, this.rootLayerPlan, {
563
+ const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, {
507
564
  type: "subscription",
565
+ parentLayerPlan: this.rootLayerPlan,
508
566
  });
509
- const $__item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => new index_js_1.__ItemStep(subscribeStep));
567
+ const $__item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, planningPath, () => new index_js_1.__ItemStep(subscribeStep));
510
568
  subscriptionEventLayerPlan.setRootStep($__item);
511
- let streamItemPlan = (0, utils_js_1.hasItemPlan)(subscribeStep)
512
- ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, subscribeStep.itemPlan, subscribeStep, $__item)
569
+ const streamItemPlan = (0, utils_js_1.hasItemPlan)(subscribeStep)
570
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(subscriptionEventLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, planningPath, subscribeStep.itemPlan, subscribeStep, $__item)
513
571
  : $__item;
514
- // WE MUST RE-FETCH STEPS AFTER DEDUPLICATION!
515
- this.deduplicateSteps();
516
- streamItemPlan = this.stepTracker.getStepById(streamItemPlan.id);
517
572
  const outputPlan = new OutputPlan_js_1.OutputPlan(subscriptionEventLayerPlan, this.rootValueStep, { mode: "root", typeName: rootType.name }, locationDetails);
518
573
  this.rootOutputPlan = outputPlan;
519
- this.planSelectionSet(outputPlan, [], exports.POLYMORPHIC_ROOT_PATH, exports.POLYMORPHIC_ROOT_PATHS, streamItemPlan, rootType, selectionSet.selections, true);
574
+ this.queueNextLayer(this.planSelectionSet, {
575
+ outputPlan,
576
+ path: [],
577
+ planningPath,
578
+ polymorphicPaths: exports.POLYMORPHIC_ROOT_PATHS,
579
+ parentStep: streamItemPlan,
580
+ positionType: rootType,
581
+ layerPlan: subscriptionEventLayerPlan,
582
+ selections: selectionSet.selections,
583
+ resolverEmulation: true,
584
+ });
585
+ this.planPending();
520
586
  }
521
587
  if (this.loc !== null)
522
588
  this.loc.pop();
523
589
  }
590
+ getCombinedLayerPlanForLayerPlans(setOfParentLayerPlans) {
591
+ const parentLayerPlans = [...setOfParentLayerPlans];
592
+ // See if one already exists with the same layer plans in the same order
593
+ for (const lp of this.stepTracker.layerPlans) {
594
+ if (lp?.reason.type === "combined" &&
595
+ (0, utils_js_1.arraysMatch)(parentLayerPlans, lp.reason.parentLayerPlans)) {
596
+ return lp;
597
+ }
598
+ }
599
+ return new LayerPlan_js_1.LayerPlan(this, {
600
+ type: "combined",
601
+ parentLayerPlans,
602
+ });
603
+ }
524
604
  /**
525
605
  * Gets the item plan for a given parent list plan - this ensures we only
526
606
  * create one item plan per parent plan.
527
607
  */
528
- itemStepForListStep(parentLayerPlan, listStep, depth, stream) {
529
- const itemStepId = this.itemStepIdByListStepId[listStep.id];
608
+ itemStepForListStep(parentLayerPlan, planningPath, listStep, depth, stream) {
609
+ const itemStepIdByListStepId = (this.itemStepIdByListStepIdByParentLayerPlanId[parentLayerPlan.id] ??=
610
+ Object.create(null));
611
+ const itemStepId = itemStepIdByListStepId[listStep.id];
530
612
  if (itemStepId !== undefined) {
531
- return this.stepTracker.getStepById(itemStepId);
613
+ const itemStep = this.stepTracker.getStepById(itemStepId);
614
+ if (listStep.polymorphicPaths !== null &&
615
+ itemStep.polymorphicPaths !== null) {
616
+ for (const p of listStep.polymorphicPaths) {
617
+ itemStep.polymorphicPaths.add(p);
618
+ }
619
+ }
620
+ return itemStep;
532
621
  }
533
622
  // Create a new LayerPlan for this list item
534
- const layerPlan = new LayerPlan_js_1.LayerPlan(this, parentLayerPlan, {
623
+ const layerPlan = new LayerPlan_js_1.LayerPlan(this, {
535
624
  type: "listItem",
625
+ parentLayerPlan,
536
626
  parentStep: listStep,
537
627
  stream,
538
628
  });
539
- const itemPlan = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, listStep.polymorphicPaths, () => new index_js_1.__ItemStep(listStep, depth));
540
- layerPlan.setRootStep(itemPlan);
541
- this.itemStepIdByListStepId[listStep.id] = itemPlan.id;
542
- return itemPlan;
543
- }
544
- processGroupedFieldSet(
545
- // Deliberately shadows
546
- outputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, objectTypeFields, isMutation, groupedFieldSet) {
629
+ const itemStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, listStep.polymorphicPaths, planningPath, () => new index_js_1.__ItemStep(listStep, depth));
630
+ layerPlan.setRootStep(itemStep);
631
+ itemStepIdByListStepId[listStep.id] = itemStep.id;
632
+ return itemStep;
633
+ }
634
+ *processGroupedFieldSet(details) {
635
+ const { outputPlan, path, planningPath, polymorphicPaths, parentStep, positionType: objectType,
636
+ // layerPlan,
637
+ objectTypeFields, isMutation, groupedFieldSet, } = details;
547
638
  // `__typename` shouldn't bump the mutation index since it has no side effects.
548
639
  let mutationIndex = -1;
549
640
  const $sideEffect = outputPlan.layerPlan.latestSideEffectStep;
@@ -600,13 +691,12 @@ class OperationPlan {
600
691
  const resolvedResolver = objectField.resolve;
601
692
  const subscriber = objectField.subscribe;
602
693
  const usesDefaultResolver = resolvedResolver == null || resolvedResolver === defaultFieldResolver;
603
- const isPolymorphic = isUnionType(namedReturnType) || isInterfaceType(namedReturnType);
604
694
  // We should use a resolver if:
605
695
  // 1. they give us a non-default resolver
606
- // 2. we're emulating resolvers AND the field is polymorphic
696
+ // 2. we're emulating resolvers
607
697
  const resolver = resolvedResolver && !usesDefaultResolver
608
698
  ? resolvedResolver
609
- : resolverEmulation && isPolymorphic
699
+ : resolverEmulation
610
700
  ? defaultFieldResolver
611
701
  : null;
612
702
  // Apply a default plan to fields that do not have a plan nor a resolver.
@@ -683,16 +773,18 @@ class OperationPlan {
683
773
  let step;
684
774
  let haltTree = false;
685
775
  const fieldLayerPlan = isMutation
686
- ? new LayerPlan_js_1.LayerPlan(this, outputPlan.layerPlan, {
776
+ ? new LayerPlan_js_1.LayerPlan(this, {
687
777
  type: "mutationField",
778
+ parentLayerPlan: outputPlan.layerPlan,
688
779
  mutationIndex: ++mutationIndex,
689
780
  })
690
781
  : outputPlan.layerPlan;
691
782
  const objectFieldArgs = objectField.args;
783
+ const fieldPath = [...path, responseKey];
784
+ const fieldPlanningPath = planningPath + responseKey;
692
785
  const trackedArguments = objectFieldArgs.length > 0
693
- ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, this.getTrackedArguments, this, objectFieldArgs, field)
786
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, fieldPlanningPath, this.getTrackedArguments, this, objectFieldArgs, field)
694
787
  : NO_ARGS;
695
- const fieldPath = [...path, responseKey];
696
788
  let streamDetails = null;
697
789
  const isList = isListType(getNullableType(fieldType));
698
790
  if (isList) {
@@ -735,7 +827,20 @@ class OperationPlan {
735
827
  }
736
828
  }
737
829
  if (typeof planResolver === "function") {
738
- ({ step, haltTree } = this.planField(objectType.name, fieldName, fieldLayerPlan, fieldPath, polymorphicPaths, planResolver, "plan", parentStep, objectField, trackedArguments, isList ? (streamDetails ?? false) : null));
830
+ ({ step, haltTree } = yield this.batchPlanField({
831
+ typeName: objectType.name,
832
+ fieldName,
833
+ layerPlan: fieldLayerPlan,
834
+ path: fieldPath,
835
+ polymorphicPaths,
836
+ planningPath: fieldPlanningPath,
837
+ planResolver,
838
+ applyAfterMode: "plan",
839
+ rawParentStep: parentStep,
840
+ field: objectField,
841
+ trackedArguments,
842
+ streamDetails: isList ? (streamDetails ?? false) : null,
843
+ }));
739
844
  }
740
845
  else {
741
846
  // No plan resolver (or plan resolver fallback) so there must be a
@@ -749,25 +854,21 @@ class OperationPlan {
749
854
  step = parentStep;
750
855
  }
751
856
  if (resolver !== null) {
752
- step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(fieldLayerPlan, polymorphicPaths, () => {
753
- const $args = (0, index_js_1.object)(field.arguments?.reduce((memo, arg) => {
754
- memo[arg.name.value] = trackedArguments.get(arg.name.value);
755
- return memo;
756
- }, Object.create(null)) ?? Object.create(null));
857
+ step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(fieldLayerPlan, polymorphicPaths, fieldPlanningPath, () => {
858
+ const $args = (0, index_js_1.object)(trackedArguments);
757
859
  return (0, graphqlResolver_js_1.graphqlResolver)(resolver, subscriber, step, $args, {
860
+ ...this.resolveInfoOperationBase,
758
861
  fieldName,
759
862
  fieldNodes,
760
- fragments: this.fragments,
761
- operation: this.operation,
762
863
  parentType: objectType,
763
864
  returnType: fieldType,
764
- schema: this.schema,
765
865
  });
766
866
  });
767
867
  }
768
868
  // May have changed due to deduplicate
769
869
  step = this.stepTracker.getStepById(step.id);
770
870
  if (haltTree) {
871
+ // Very similar to this.handlePlanningError. Should probably use it in future.
771
872
  const isNonNull = isNonNullType(fieldType);
772
873
  outputPlan.addChild(objectType, responseKey, {
773
874
  type: "outputPlan",
@@ -777,11 +878,31 @@ class OperationPlan {
777
878
  });
778
879
  }
779
880
  else {
780
- this.planIntoOutputPlan(outputPlan, fieldLayerPlan, fieldPath, polymorphicPath, polymorphicPaths,
781
- // If one field has a selection set, they all have a selection set (guaranteed by validation).
782
- field.selectionSet != null
783
- ? fieldNodes.flatMap((n) => n.selectionSet.selections)
784
- : undefined, objectType, responseKey, fieldType, step, locationDetails, resolverEmulation, 0, streamDetails);
881
+ if (index_js_1.isDev && polymorphicPaths) {
882
+ const invalid = [...polymorphicPaths].filter((p) => !stepIsValidInPolyPath(step, p));
883
+ if (invalid.length > 0) {
884
+ throw new Error(`${objectType}.${fieldName} (as ${responseKey}) returned ${step}, but that's not valid in ${invalid.length} out of ${polymorphicPaths.size} of the expected polymorphic paths. Invalid paths: ${invalid}`);
885
+ }
886
+ }
887
+ outputPlan.expectChild(objectType, responseKey);
888
+ this.queueNextLayer(this.planFieldReturnType, {
889
+ outputPlan,
890
+ path: fieldPath,
891
+ planningPath: fieldPlanningPath,
892
+ polymorphicPaths,
893
+ parentStep: step,
894
+ positionType: fieldType,
895
+ layerPlan: fieldLayerPlan,
896
+ // If one field has a selection set, they all have a selection set (guaranteed by validation).
897
+ selections: field.selectionSet != null
898
+ ? fieldNodes.flatMap((n) => n.selectionSet.selections)
899
+ : undefined,
900
+ parentObjectType: objectType,
901
+ responseKey,
902
+ locationDetails,
903
+ resolverEmulation,
904
+ streamDetails,
905
+ });
785
906
  }
786
907
  }
787
908
  finally {
@@ -790,8 +911,9 @@ class OperationPlan {
790
911
  }
791
912
  if (groupedFieldSet.deferred !== undefined) {
792
913
  for (const deferred of groupedFieldSet.deferred) {
793
- const deferredLayerPlan = new LayerPlan_js_1.LayerPlan(this, outputPlan.layerPlan, {
914
+ const deferredLayerPlan = new LayerPlan_js_1.LayerPlan(this, {
794
915
  type: "defer",
916
+ parentLayerPlan: outputPlan.layerPlan,
795
917
  label: deferred.label,
796
918
  });
797
919
  const deferredOutputPlan = new OutputPlan_js_1.OutputPlan(deferredLayerPlan, outputPlan.rootStep, {
@@ -805,7 +927,18 @@ class OperationPlan {
805
927
  const $sideEffect = deferredOutputPlan.layerPlan.latestSideEffectStep;
806
928
  try {
807
929
  outputPlan.deferredOutputPlans.push(deferredOutputPlan);
808
- this.processGroupedFieldSet(deferredOutputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, objectTypeFields, isMutation, deferred);
930
+ this.queueNextLayer(this.processGroupedFieldSet, {
931
+ outputPlan: deferredOutputPlan,
932
+ path,
933
+ planningPath: planningPath + "#",
934
+ polymorphicPaths,
935
+ parentStep,
936
+ positionType: objectType,
937
+ layerPlan: deferredLayerPlan,
938
+ objectTypeFields,
939
+ isMutation,
940
+ groupedFieldSet: deferred,
941
+ });
809
942
  }
810
943
  finally {
811
944
  deferredOutputPlan.layerPlan.latestSideEffectStep = $sideEffect;
@@ -822,33 +955,432 @@ class OperationPlan {
822
955
  * @param selections - The GraphQL selections (fields, fragment spreads, inline fragments) to evaluate
823
956
  * @param isMutation - If true this selection set should be executed serially rather than in parallel (each field gets its own LayerPlan)
824
957
  */
825
- planSelectionSet(outputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, selections, resolverEmulation, isMutation = false) {
958
+ planSelectionSet(details) {
959
+ const { outputPlan, path, planningPath, polymorphicPaths, parentStep, positionType: objectType, layerPlan, selections, resolverEmulation, isMutation = false, } = details;
826
960
  if (this.loc !== null) {
827
- this.loc.push(`planSelectionSet(${objectType.name} @ ${outputPlan.layerPlan.id} @ ${path.join(".")} @ ${polymorphicPath ?? ""})`);
961
+ this.loc.push(`planSelectionSet(${objectType.name} @ ${outputPlan.layerPlan.id} @ ${path.join(".")} @ ${polymorphicPaths ? [...polymorphicPaths] : ""})`);
828
962
  }
829
963
  if (index_js_1.isDev) {
830
964
  assertObjectType(objectType);
965
+ if (outputPlan.layerPlan !== layerPlan) {
966
+ throw new Error(`GrafastInternalError<3ea1a3dd-1e11-4eb7-a31b-e125996d7eb4>: expected ${outputPlan} to have layer plan ${layerPlan}`);
967
+ }
831
968
  }
832
- const groupedFieldSet = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(outputPlan.layerPlan, polymorphicPaths, graphqlCollectFields_js_1.graphqlCollectFields, null, this, parentStep.id, objectType, selections, (0, graphqlCollectFields_js_1.newSelectionSetDigest)(resolverEmulation), isMutation);
969
+ const groupedFieldSet = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, planningPath, graphqlCollectFields_js_1.graphqlCollectFields, null, this, parentStep.id, objectType, selections, (0, graphqlCollectFields_js_1.newSelectionSetDigest)(resolverEmulation), isMutation);
833
970
  const objectTypeFields = objectType.getFields();
834
- this.processGroupedFieldSet(outputPlan, path, polymorphicPath, polymorphicPaths, parentStep, objectType, objectTypeFields, isMutation, groupedFieldSet);
971
+ this.queueNextLayer(this.processGroupedFieldSet, {
972
+ outputPlan,
973
+ path,
974
+ planningPath: planningPath + ">",
975
+ polymorphicPaths,
976
+ parentStep,
977
+ positionType: objectType,
978
+ layerPlan,
979
+ objectTypeFields,
980
+ isMutation,
981
+ groupedFieldSet,
982
+ });
835
983
  if (this.loc !== null)
836
984
  this.loc.pop();
837
985
  }
986
+ queueNextLayer(method, details) {
987
+ const { planningPath } = details;
988
+ if (this.frozenPlanningPaths?.has(planningPath)) {
989
+ throw new Error(`GrafastInternalError<93c64f55-4f3f-442f-a17b-391e28bd3629>: Already processed planning path ${planningPath}`);
990
+ }
991
+ let list = this.planningQueueByPlanningPath.get(planningPath);
992
+ if (!list) {
993
+ list = [];
994
+ this.planningQueueByPlanningPath.set(planningPath, list);
995
+ }
996
+ const tuple = [
997
+ method,
998
+ details,
999
+ this.loc ? [...this.loc] : null,
1000
+ details.layerPlan,
1001
+ details.layerPlan.latestSideEffectStep,
1002
+ ];
1003
+ this.planningQueue.push(tuple);
1004
+ list.push(tuple);
1005
+ }
1006
+ planPending() {
1007
+ for (let depth = 0; depth <= this.maxPlanningDepth; depth++) {
1008
+ // Process the next batch
1009
+ const l = this.planningQueue.length;
1010
+ if (l === 0)
1011
+ break;
1012
+ if (depth === this.maxPlanningDepth) {
1013
+ throw new Error(`Query is too complex, Grafast refuses to continue processing at planning depth ${this.maxPlanningDepth}. You can configure this through \`preset.grafast.maxPlanningDepth\``);
1014
+ }
1015
+ // NOTE: `batch` and `todo` are different views of the same information,
1016
+ // specifically: `batch` is stored in the order in which the calls were
1017
+ // made (critical for ensuring the OutputPlans output fields in the
1018
+ // correct order); whereas `todo` is the result of grouping `batch` into
1019
+ // groups where the planning paths match.
1020
+ const batch = this.planningQueue.slice(0, l);
1021
+ const todo = [...this.planningQueueByPlanningPath.entries()];
1022
+ // Now clear the stores ready to start collecting the next lot.
1023
+ this.planningQueue.length = 0;
1024
+ this.planningQueueByPlanningPath.clear();
1025
+ // First, do a planning-path-aware deduplicate
1026
+ this.deduplicateSteps();
1027
+ // Update the step references (and freeze the paths)
1028
+ for (const [planningPath, batch] of todo) {
1029
+ if (this.frozenPlanningPaths?.has(planningPath)) {
1030
+ throw new Error(`GrafastInternalError<dd30288e-9375-4025-91a6-66b64e74cf74>: planning path already processed - ${planningPath}`);
1031
+ }
1032
+ this.frozenPlanningPaths?.add(planningPath);
1033
+ for (const entry of batch) {
1034
+ const step = entry[1].parentStep;
1035
+ const actualStep = this.stepTracker.getStepById(step.id);
1036
+ if (actualStep !== step) {
1037
+ entry[1].parentStep = actualStep;
1038
+ }
1039
+ }
1040
+ }
1041
+ // Then, apply the per-method batch tweaks (such as resolving
1042
+ // polymorphism)
1043
+ this.mutateTodos(todo);
1044
+ const DONE = Symbol("done");
1045
+ const gennies = [];
1046
+ // Finally plan this layer
1047
+ for (const [fn, rawDetails, loc, originalLayerPlan, layerPlanLatestSideEffect,] of batch) {
1048
+ const details = rawDetails;
1049
+ this.withLocLpSE(loc, originalLayerPlan, layerPlanLatestSideEffect, () => {
1050
+ /** Still just genny from the block */
1051
+ const genny = fn.call(this, details);
1052
+ if (genny != null) {
1053
+ gennies.push({
1054
+ genny,
1055
+ loc,
1056
+ layerPlan: details.layerPlan,
1057
+ layerPlanLatestSideEffect: details.layerPlan.latestSideEffectStep,
1058
+ result: undefined,
1059
+ });
1060
+ }
1061
+ });
1062
+ }
1063
+ // And go through all the generators
1064
+ while (gennies.length > 0) {
1065
+ const cbs = [];
1066
+ for (const spec of gennies) {
1067
+ const { genny, loc, layerPlan, layerPlanLatestSideEffect, result } = spec;
1068
+ this.withLocLpSE(loc, layerPlan, layerPlanLatestSideEffect, () => {
1069
+ const r = genny.next(result);
1070
+ if (r.done) {
1071
+ spec.result = DONE;
1072
+ }
1073
+ else {
1074
+ cbs.push({ spec, cb: r.value });
1075
+ }
1076
+ });
1077
+ }
1078
+ for (const { spec, cb } of cbs) {
1079
+ spec.result = cb();
1080
+ }
1081
+ // Remove the generators that are done.
1082
+ for (let i = gennies.length - 1; i >= 0; i--) {
1083
+ if (gennies[i].result === DONE) {
1084
+ gennies.splice(i, 1);
1085
+ }
1086
+ }
1087
+ }
1088
+ }
1089
+ // A final deduplicate
1090
+ this.deduplicateSteps();
1091
+ }
1092
+ withLocLpSE(loc, layerPlan, layerPlanLatestSideEffect, cb) {
1093
+ const $sideEffect = layerPlan.latestSideEffectStep;
1094
+ layerPlan.latestSideEffectStep = layerPlanLatestSideEffect;
1095
+ try {
1096
+ const prevLoc = this.loc;
1097
+ this.loc = loc;
1098
+ cb();
1099
+ this.loc = prevLoc;
1100
+ }
1101
+ finally {
1102
+ layerPlan.latestSideEffectStep = $sideEffect;
1103
+ }
1104
+ }
1105
+ mutateTodos(todo) {
1106
+ for (const [_planningPath, batch] of todo) {
1107
+ const polymorphicResolveTypeEntriesByPolyType = new Map();
1108
+ const planFieldReturnTypeEntriesByStepByLayerPlan = new Map();
1109
+ for (const entry of batch) {
1110
+ const [method, rawArgs] = entry;
1111
+ // Tidy up after dedupe
1112
+ rawArgs.parentStep = this.stepTracker.getStepById(rawArgs.parentStep.id);
1113
+ if (method === this.polymorphicResolveType) {
1114
+ const args = rawArgs;
1115
+ const polyType = args.positionType;
1116
+ if (!isUnionType(polyType) && !isInterfaceType(polyType)) {
1117
+ throw new Error(`GrafastInternalError<b0a276d0-134f-4a26-9c33-00af5ead38e6>: expected ${polyType} to be an interface or union type`);
1118
+ }
1119
+ let list = polymorphicResolveTypeEntriesByPolyType.get(polyType);
1120
+ if (!list) {
1121
+ list = [];
1122
+ polymorphicResolveTypeEntriesByPolyType.set(polyType, list);
1123
+ }
1124
+ list.push(args);
1125
+ }
1126
+ else if (method == this.polymorphicPlanObjectType) {
1127
+ const args = rawArgs;
1128
+ const lp = args.layerPlan;
1129
+ let planFieldReturnTypeEntriesByStep = planFieldReturnTypeEntriesByStepByLayerPlan.get(lp);
1130
+ if (!planFieldReturnTypeEntriesByStep) {
1131
+ planFieldReturnTypeEntriesByStep = new Map();
1132
+ planFieldReturnTypeEntriesByStepByLayerPlan.set(lp, planFieldReturnTypeEntriesByStep);
1133
+ }
1134
+ const step = args.parentStep;
1135
+ let list = planFieldReturnTypeEntriesByStep.get(step);
1136
+ if (list) {
1137
+ list.push(args);
1138
+ }
1139
+ else {
1140
+ list = [args];
1141
+ planFieldReturnTypeEntriesByStep.set(step, list);
1142
+ }
1143
+ }
1144
+ }
1145
+ // Handle calls to `polymorphicResolveType` by performing the combined
1146
+ // layer plan / `planType` shuffle.
1147
+ for (const [graphqlType,
1148
+ // A list of all the `details` argument objects capture for this type
1149
+ detailsRecordList,] of polymorphicResolveTypeEntriesByPolyType) {
1150
+ const firstDetailsRecord = detailsRecordList[0];
1151
+ // All of these properties should be in common
1152
+ // const path = firstDetailsRecord.path;
1153
+ const planningPath = firstDetailsRecord.planningPath;
1154
+ const allPossibleObjectTypes = firstDetailsRecord.allPossibleObjectTypes;
1155
+ // The existence of this is common (i.e. either all have it set, or all
1156
+ // have it null, but the actual contents may differ)
1157
+ const polymorphicPaths = firstDetailsRecord.polymorphicPaths;
1158
+ const combinedPolymorphicPaths = polymorphicPaths
1159
+ ? new Set( /* Populated below */)
1160
+ : null;
1161
+ let commonLayerPlan;
1162
+ let $original;
1163
+ let commonStep;
1164
+ const steps = new Set(detailsRecordList.map((t) => t.parentStep));
1165
+ if (steps.size > 1) {
1166
+ $original = null;
1167
+ // Make combined
1168
+ const layerPlans = new Set(detailsRecordList.map((t) => t.layerPlan));
1169
+ const combinedLayerPlan = this.getCombinedLayerPlanForLayerPlans(layerPlans);
1170
+ const toCombine = [];
1171
+ let isUnary = true;
1172
+ for (const detailsRecord of detailsRecordList) {
1173
+ const parentStep = detailsRecord.parentStep;
1174
+ const polymorphicPaths = detailsRecord.polymorphicPaths;
1175
+ const layerPlan = detailsRecord.layerPlan;
1176
+ if (!parentStep._isUnary) {
1177
+ isUnary = false;
1178
+ }
1179
+ if (polymorphicPaths) {
1180
+ for (const path of polymorphicPaths) {
1181
+ combinedPolymorphicPaths.add(path);
1182
+ }
1183
+ }
1184
+ const $data = graphqlType.extensions?.grafast?.toSpecifier
1185
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, planningPath, graphqlType.extensions.grafast.toSpecifier, graphqlType.extensions.grafast, parentStep)
1186
+ : (0, step_js_1.stepHasToSpecifier)(parentStep)
1187
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, planningPath, parentStep.toSpecifier, parentStep)
1188
+ : (0, step_js_1.stepHasToRecord)(parentStep)
1189
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, planningPath, parentStep.toRecord, parentStep)
1190
+ : parentStep;
1191
+ toCombine.push({ step: $data, layerPlan });
1192
+ }
1193
+ // Create a __ValueStep in the new combined layer plan that
1194
+ // represents all of these values
1195
+ const $combined = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(combinedLayerPlan, combinedPolymorphicPaths, planningPath, newValueStepCallback, null, false);
1196
+ if (!isUnary) {
1197
+ $combined._isUnary = false;
1198
+ }
1199
+ // Tell it to populate the __ValuePlan $combined with the combination
1200
+ // of all the values from toCombine.
1201
+ combinedLayerPlan.addCombo(toCombine, $combined);
1202
+ commonLayerPlan = combinedLayerPlan;
1203
+ commonStep = $combined;
1204
+ }
1205
+ else {
1206
+ $original = [...steps][0];
1207
+ const layerPlans = new Set();
1208
+ for (const tuple of detailsRecordList) {
1209
+ layerPlans.add(tuple.layerPlan);
1210
+ }
1211
+ if (layerPlans.size === 1) {
1212
+ commonLayerPlan = [...layerPlans][0];
1213
+ }
1214
+ else {
1215
+ commonLayerPlan =
1216
+ this.getCombinedLayerPlanForLayerPlans(layerPlans);
1217
+ }
1218
+ commonStep = graphqlType.extensions?.grafast?.toSpecifier
1219
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(commonLayerPlan, polymorphicPaths, planningPath, graphqlType.extensions.grafast.toSpecifier, graphqlType.extensions.grafast, $original)
1220
+ : (0, step_js_1.stepHasToSpecifier)($original)
1221
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(commonLayerPlan, polymorphicPaths, planningPath, $original.toSpecifier, $original)
1222
+ : (0, step_js_1.stepHasToRecord)($original)
1223
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(commonLayerPlan, polymorphicPaths, planningPath, $original.toRecord, $original)
1224
+ : $original;
1225
+ for (const detailsRecord of detailsRecordList) {
1226
+ detailsRecord.layerPlan = commonLayerPlan;
1227
+ const polymorphicPaths = detailsRecord.polymorphicPaths;
1228
+ if (polymorphicPaths) {
1229
+ for (const path of polymorphicPaths) {
1230
+ combinedPolymorphicPaths.add(path);
1231
+ }
1232
+ }
1233
+ }
1234
+ }
1235
+ // Call planTypes and plan each of the types?
1236
+ const planType = graphqlType.extensions?.grafast?.planType ?? defaultPlanType;
1237
+ // TODO: revisit this. If planType exists then no need? If resolver emulation then always?
1238
+ const resolverEmulation =
1239
+ // detailsRecordList.some((a) => a.resolverEmulation) && (
1240
+ graphqlType.resolveType != null ||
1241
+ allPossibleObjectTypes.some((t) => t.isTypeOf != null);
1242
+ const info = {
1243
+ abstractType: graphqlType,
1244
+ resolverEmulation,
1245
+ $original,
1246
+ };
1247
+ let polymorphicTypePlanner;
1248
+ try {
1249
+ polymorphicTypePlanner = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(commonLayerPlan, combinedPolymorphicPaths, planningPath, planType, null, commonStep, info);
1250
+ }
1251
+ catch (err) {
1252
+ const $error = this.withRootLayerPlan(() => (0, index_js_1.error)(err));
1253
+ polymorphicTypePlanner = {
1254
+ $__typename: $error,
1255
+ };
1256
+ }
1257
+ const stepForType = new Map();
1258
+ const allTypeNames = allPossibleObjectTypes.map((t) => t.name);
1259
+ const basePaths = [...(combinedPolymorphicPaths ?? [""])];
1260
+ const polymorphicLayerPlan = new LayerPlan_js_1.LayerPlan(this, {
1261
+ type: "polymorphic",
1262
+ parentLayerPlan: commonLayerPlan,
1263
+ polymorphicPaths: new Set(
1264
+ // BUG: this isn't 100% correct, it's possible that this layer
1265
+ // plan might not be used in some of these polymorphic paths (e.g.
1266
+ // if fragment types caused certain fields to not be selected in
1267
+ // certain scenarios). We should instead figure out the concrete
1268
+ // list of polymorphic paths actually seen during planning.
1269
+ allTypeNames.flatMap((t) => basePaths.map((p) => `${p}>${t}`))),
1270
+ typeNames: allTypeNames,
1271
+ parentStep: polymorphicTypePlanner.$__typename,
1272
+ });
1273
+ for (const type of allPossibleObjectTypes) {
1274
+ const polymorphicPaths = new Set(basePaths.map((p) => `${p}>${type.name}`));
1275
+ try {
1276
+ const $stepForType = (polymorphicTypePlanner.planForType
1277
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(polymorphicLayerPlan, polymorphicPaths, planningPath + "?", polymorphicTypePlanner.planForType, polymorphicTypePlanner, type)
1278
+ : type.extensions?.grafast?.planType
1279
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(polymorphicLayerPlan, polymorphicPaths, planningPath + "?", type.extensions.grafast.planType, type.extensions.grafast, commonStep)
1280
+ : commonStep) ??
1281
+ (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(polymorphicLayerPlan, polymorphicPaths, planningPath + "?", constant_js_1.constant, null, index_js_1.$$inhibit);
1282
+ stepForType.set(type, $stepForType);
1283
+ if (index_js_1.isDev) {
1284
+ // Check that this plan is compatible with every poly path
1285
+ if ($stepForType.polymorphicPaths === null) {
1286
+ // All good, valid in all paths
1287
+ }
1288
+ else {
1289
+ const missedPaths = [...polymorphicPaths].filter((p) => !stepIsValidInPolyPath($stepForType, p));
1290
+ if (missedPaths.length > 0) {
1291
+ throw new Error(`When planning ${graphqlType}'s planForType for ${type.name}, returned step ${$stepForType} is not valid in ${missedPaths.length} out of ${polymorphicPaths.size} expected paths; missed paths: ${missedPaths}`);
1292
+ }
1293
+ }
1294
+ }
1295
+ }
1296
+ catch (e) {
1297
+ stepForType.set(type, e);
1298
+ }
1299
+ }
1300
+ // Now replace references to layer plan and step
1301
+ for (const detailsRecord of detailsRecordList) {
1302
+ detailsRecord.parentStep = commonStep;
1303
+ detailsRecord.layerPlan = polymorphicLayerPlan;
1304
+ // TODO: do we want to set the polymorphic paths here or not?
1305
+ // detailsRecord.polymorphicPaths = combinedPolymorphicPaths;
1306
+ const { locationDetails, parentObjectType, responseKey, isNonNull, outputPlan: parentOutputPlan, } = detailsRecord;
1307
+ const polymorphicOutputPlan = new OutputPlan_js_1.OutputPlan(commonLayerPlan, polymorphicTypePlanner.$__typename, {
1308
+ mode: "polymorphic",
1309
+ deferLabel: undefined,
1310
+ typeNames: allPossibleObjectTypes.map((t) => t.name),
1311
+ }, locationDetails);
1312
+ detailsRecord.outputPlan = polymorphicOutputPlan;
1313
+ parentOutputPlan.addChild(parentObjectType, responseKey, {
1314
+ type: "outputPlan",
1315
+ outputPlan: polymorphicOutputPlan,
1316
+ isNonNull,
1317
+ locationDetails,
1318
+ });
1319
+ detailsRecord.stepForType = stepForType;
1320
+ }
1321
+ }
1322
+ for (const [parentLayerPlan, planFieldReturnTypeEntriesByStep,] of planFieldReturnTypeEntriesByStepByLayerPlan) {
1323
+ if (planFieldReturnTypeEntriesByStep.size <= 1) {
1324
+ continue;
1325
+ }
1326
+ for (const [step, entries] of planFieldReturnTypeEntriesByStep) {
1327
+ // We already know the planningPath lines up.
1328
+ // We know all `entries` resolved to the same `step`.
1329
+ // We know all `entries` come from the same layer plan.
1330
+ // TODO: I'm not sure if we care if the field result type is the
1331
+ // same or not. I don't think so?
1332
+ // We want to create a polymorphicPartition layer plan for these, if
1333
+ // it makes sense.
1334
+ // It makes sense if there are multiple different groups; which we
1335
+ // checked in the `if` above. So, yes.
1336
+ // TODO: Check assumption: the different groups should relate to
1337
+ // different types.
1338
+ const typeNameSet = new Set();
1339
+ entries.forEach((e) => void typeNameSet.add(e.positionType.name));
1340
+ const typeNames = [...typeNameSet];
1341
+ // TODO: eliminate this
1342
+ const polymorphicPaths = new Set(entries.flatMap((e) => [...(e.polymorphicPaths ?? [])]));
1343
+ const partitionedLayerPlan = parentLayerPlan.getPartition(typeNames, polymorphicPaths);
1344
+ /**
1345
+ * Moves all the steps that match these polymorphic paths only into
1346
+ * the polymorphicPartition layer plan
1347
+ */
1348
+ const recursivelyMoveSteps = (step) => {
1349
+ if (step.layerPlan !== parentLayerPlan)
1350
+ return;
1351
+ if (!(0, utils_js_1.setsMatch)(step.polymorphicPaths, polymorphicPaths))
1352
+ return;
1353
+ this.stepTracker.moveStepToLayerPlan(step, partitionedLayerPlan);
1354
+ for (const dep of (0, utils_js_1.sudo)(step).dependencies) {
1355
+ recursivelyMoveSteps(dep);
1356
+ }
1357
+ };
1358
+ recursivelyMoveSteps(step);
1359
+ // TODO: Do we need to update the outputPlan.layerPlan too?
1360
+ for (const entry of entries) {
1361
+ entry.layerPlan = partitionedLayerPlan;
1362
+ }
1363
+ }
1364
+ }
1365
+ }
1366
+ }
838
1367
  internalDependency($step) {
839
1368
  this.stepTracker.internalDependencies.add($step);
840
1369
  return $step;
841
1370
  }
842
- // Similar to the old 'planFieldReturnType'
843
- planIntoOutputPlan(parentOutputPlan,
844
- // Typically this is parentOutputPlan.layerPlan; but in the case of mutationFields it isn't.
845
- parentLayerPlan,
846
- // This is the LAYER-RELATIVE path, not the absolute path! It resets!
847
- path, polymorphicPath, polymorphicPaths, selections, parentObjectType, responseKey, fieldType, $step, locationDetails, resolverEmulation, listDepth, streamDetails) {
1371
+ planFieldReturnType(details) {
1372
+ return this.planIntoOutputPlan({ ...details, listDepth: 0 });
1373
+ }
1374
+ planIntoOutputPlan(details) {
1375
+ const { outputPlan: parentOutputPlan, path, planningPath, polymorphicPaths, parentStep: $step, positionType: fieldType, layerPlan: parentLayerPlan, selections, parentObjectType, responseKey, locationDetails, resolverEmulation, listDepth, streamDetails, } = details;
848
1376
  const nullableFieldType = getNullableType(fieldType);
849
1377
  const isNonNull = nullableFieldType !== fieldType;
850
1378
  if (isListType(nullableFieldType)) {
851
- const $list = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(parentLayerPlan, polymorphicPaths, connection_js_1.itemsOrStep, null, $step);
1379
+ const listItemPlanningPath = planningPath +
1380
+ `[${streamDetails
1381
+ ? `#${streamDetails.initialCount.id}|${streamDetails.if.id}|${streamDetails.label.id}`
1382
+ : ""}]`;
1383
+ const $list = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(parentLayerPlan, polymorphicPaths, listItemPlanningPath, connection_js_1.itemsOrStep, null, $step);
852
1384
  if ($list !== $step) {
853
1385
  $list._stepOptions.stream = $step._stepOptions.stream;
854
1386
  }
@@ -868,25 +1400,20 @@ class OperationPlan {
868
1400
  labelStepId: streamDetails.label.id,
869
1401
  }
870
1402
  : undefined;
871
- let $__item = this.itemStepForListStep(parentLayerPlan, $list, listDepth, stream);
872
- const $sideEffect = $__item.layerPlan.latestSideEffectStep;
873
- try {
874
- let $item;
875
- if ((0, step_js_1.isListCapableStep)($list)) {
876
- $item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)($__item.layerPlan, $__item.polymorphicPaths, $list.listItem, $list, $__item);
877
- this.deduplicateSteps();
878
- // Refetch steps following deduplicate
879
- $__item = this.stepTracker.getStepById($__item.id);
880
- $item = this.stepTracker.getStepById($item.id);
881
- }
882
- else {
883
- $item = $__item;
884
- }
885
- this.planIntoOutputPlan(listOutputPlan, $item.layerPlan, path, polymorphicPath, polymorphicPaths, selections, null, null, nullableFieldType.ofType, $item, locationDetails, resolverEmulation, listDepth + 1, null);
886
- }
887
- finally {
888
- $__item.layerPlan.latestSideEffectStep = $sideEffect;
889
- }
1403
+ this.queueNextLayer(this.planListItem, {
1404
+ outputPlan: listOutputPlan,
1405
+ path,
1406
+ planningPath: listItemPlanningPath,
1407
+ polymorphicPaths,
1408
+ parentStep: $list,
1409
+ positionType: nullableFieldType,
1410
+ layerPlan: parentLayerPlan,
1411
+ selections,
1412
+ listDepth,
1413
+ stream,
1414
+ locationDetails,
1415
+ resolverEmulation,
1416
+ });
890
1417
  }
891
1418
  else if (isScalarType(nullableFieldType)) {
892
1419
  const scalarPlanResolver = nullableFieldType.extensions?.grafast?.plan;
@@ -895,7 +1422,7 @@ class OperationPlan {
895
1422
  const $sideEffect = parentLayerPlan.latestSideEffectStep;
896
1423
  try {
897
1424
  const $leaf = typeof scalarPlanResolver === "function"
898
- ? (0, utils_js_1.assertNotPromise)((0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(parentLayerPlan, polymorphicPaths, scalarPlanResolver, null, $step, this.scalarPlanInfo), scalarPlanResolver, fnDescription)
1425
+ ? (0, utils_js_1.assertNotPromise)((0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(parentLayerPlan, polymorphicPaths, planningPath, scalarPlanResolver, null, $step, this.scalarPlanInfo), scalarPlanResolver, fnDescription)
899
1426
  : $step;
900
1427
  parentOutputPlan.addChild(parentObjectType, responseKey, {
901
1428
  type: "outputPlan",
@@ -968,8 +1495,9 @@ class OperationPlan {
968
1495
  objectLayerPlan = match;
969
1496
  }
970
1497
  else {
971
- objectLayerPlan = new LayerPlan_js_1.LayerPlan(this, parentLayerPlan, {
1498
+ objectLayerPlan = new LayerPlan_js_1.LayerPlan(this, {
972
1499
  type: "nullableBoundary",
1500
+ parentLayerPlan,
973
1501
  parentStep: $step,
974
1502
  });
975
1503
  objectLayerPlan.setRootStep($step);
@@ -988,7 +1516,17 @@ class OperationPlan {
988
1516
  isNonNull,
989
1517
  locationDetails,
990
1518
  });
991
- this.planSelectionSet(objectOutputPlan, path, polymorphicPath, polymorphicPaths, $step, nullableFieldType, selections, resolverEmulation);
1519
+ this.queueNextLayer(this.planSelectionSet, {
1520
+ outputPlan: objectOutputPlan,
1521
+ path,
1522
+ planningPath: planningPath + ".",
1523
+ polymorphicPaths,
1524
+ parentStep: $step,
1525
+ positionType: nullableFieldType,
1526
+ layerPlan: objectLayerPlan,
1527
+ selections: selections,
1528
+ resolverEmulation,
1529
+ });
992
1530
  }
993
1531
  finally {
994
1532
  objectLayerPlan.latestSideEffectStep = $sideEffect;
@@ -999,7 +1537,8 @@ class OperationPlan {
999
1537
  const isUnion = isUnionType(nullableFieldType);
1000
1538
  const isInterface = isInterfaceType(nullableFieldType);
1001
1539
  if (!(isUnion || isInterface)) {
1002
- throw new Error(`GrafastInternalError<a54d6d63-d186-4ab9-9299-05f817894300>: Wasn't expecting ${nullableFieldType}`);
1540
+ const never = nullableFieldType;
1541
+ throw new Error(`GrafastInternalError<a54d6d63-d186-4ab9-9299-05f817894300>: Wasn't expecting ${never}`);
1003
1542
  }
1004
1543
  assert.ok(selections, "GrafastInternalError<d94e281c-1a10-463e-b7f5-2b0a3665d99b>: A polymorphic type with no selections is invalid");
1005
1544
  /*
@@ -1007,10 +1546,11 @@ class OperationPlan {
1007
1546
  * other types.
1008
1547
  *
1009
1548
  * First, we figure out the list of `possibleTypes` based on the
1010
- * union/interface and any other constraints that we know. NOTE: we can't
1549
+ * union/interface and any other constraints that we know. Note: we can't
1011
1550
  * discount a type just because it doesn't have any fragments that apply
1012
1551
  * to it - instead we must still plan an empty selection set (or one just
1013
- * using `{__typename}`), and we need to know it exists for that. See
1552
+ * using `{__typename}`), and we need to know it exists (is not null) for
1553
+ * that. See
1014
1554
  * https://github.com/graphql/graphql-spec/issues/951#issuecomment-1140957685
1015
1555
  */
1016
1556
  const allPossibleObjectTypes = isUnion
@@ -1027,139 +1567,329 @@ class OperationPlan {
1027
1567
  });
1028
1568
  }
1029
1569
  else {
1030
- /*
1031
- * Next, we ensure we're dealing with a polymorphic step.
1032
- */
1033
- if (!(0, step_js_1.isPolymorphicStep)($step)) {
1034
- throw new Error(`${$step} is not a polymorphic capable step, it must have a planForType method`);
1035
- }
1036
- /*
1037
- * An output plan for it (knows how to branch the different object
1038
- * output plans).
1039
- */
1040
- const polymorphicOutputPlan = new OutputPlan_js_1.OutputPlan(parentLayerPlan, $step, {
1041
- mode: "polymorphic",
1042
- deferLabel: undefined,
1043
- typeNames: allPossibleObjectTypes.map((t) => t.name),
1044
- }, locationDetails);
1045
- parentOutputPlan.addChild(parentObjectType, responseKey, {
1046
- type: "outputPlan",
1047
- outputPlan: polymorphicOutputPlan,
1048
- isNonNull,
1570
+ const polymorphicResolvePlanningPath = planningPath + "<*>";
1571
+ this.queueNextLayer(this.polymorphicResolveType, {
1572
+ outputPlan: parentOutputPlan,
1573
+ path,
1574
+ planningPath: polymorphicResolvePlanningPath,
1575
+ polymorphicPaths,
1576
+ parentStep: $step, // NOTE: This may be batched and turned into a $specifier step
1577
+ positionType: nullableFieldType,
1578
+ layerPlan: parentLayerPlan, // NOTE: This may be batched and turned into a combined layer plan
1579
+ selections,
1580
+ allPossibleObjectTypes,
1049
1581
  locationDetails,
1582
+ parentObjectType,
1583
+ responseKey,
1584
+ isNonNull,
1585
+ resolverEmulation,
1050
1586
  });
1051
- /*
1052
- * Now a polymorphic layer plan for all the plans to live in
1053
- */
1054
- const polymorphicLayerPlan = this.getPolymorphicLayerPlan(parentLayerPlan, path, $step, allPossibleObjectTypes);
1055
- /*
1056
- * Now we need to loop through each type and plan it.
1057
- */
1058
- const polyBase = polymorphicPath ?? "";
1059
- const $oldStep = $step;
1060
- for (const type of allPossibleObjectTypes) {
1061
- const $sideEffect = polymorphicLayerPlan.latestSideEffectStep;
1062
- try {
1063
- /* TODO: this $oldStep / $step dance turned out to be necessary
1064
- * because `$step` was being replaced in the following query against
1065
- * the graphilecrystaltest `polymorpic` schema; but I'm not sure this is the
1066
- * right behavior - shouldn't deduplication have been temporarily
1067
- * disabled during this?
1068
- *
1069
- * ```graphql
1070
- * {
1071
- * allSingleTableItems {
1072
- * nodes {
1073
- * id
1074
- * type
1075
- * singleTableItemsByParentIdList {
1076
- * id
1077
- * position
1078
- * }
1079
- * }
1080
- * }
1081
- * }
1082
- * ```
1083
- */
1084
- const $step = this.stepTracker.getStepById($oldStep.id);
1085
- // Bit of a hack, but saves passing it around through all the arguments
1086
- const newPolymorphicPath = `${polyBase}>${type.name}`;
1087
- polymorphicLayerPlan.reason.polymorphicPaths.add(newPolymorphicPath);
1088
- const newPolymorphicPaths = new Set();
1089
- newPolymorphicPaths.add(newPolymorphicPath);
1090
- const $root = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(polymorphicLayerPlan, newPolymorphicPaths, $step.planForType, $step, type);
1091
- const objectOutputPlan = new OutputPlan_js_1.OutputPlan(polymorphicLayerPlan, $root, {
1092
- mode: "object",
1093
- deferLabel: undefined,
1094
- typeName: type.name,
1095
- }, locationDetails);
1096
- // find all selections compatible with `type`
1097
- const fieldNodes = (0, graphqlMergeSelectionSets_js_1.fieldSelectionsForType)(this, type, selections);
1098
- this.planSelectionSet(objectOutputPlan, path, newPolymorphicPath, newPolymorphicPaths, $root, type, fieldNodes, false);
1099
- polymorphicOutputPlan.addChild(type, null, {
1100
- type: "outputPlan",
1101
- isNonNull,
1102
- outputPlan: objectOutputPlan,
1103
- locationDetails,
1104
- });
1587
+ }
1588
+ }
1589
+ }
1590
+ /** @internal */
1591
+ planListItem(details) {
1592
+ const { outputPlan: listOutputPlan, path, planningPath: listItemPlanningPath, polymorphicPaths, parentStep: $list, positionType: nullableFieldType, layerPlan: parentLayerPlan, selections, listDepth, stream, locationDetails, resolverEmulation, } = details;
1593
+ const $__item = this.itemStepForListStep(parentLayerPlan, listItemPlanningPath, $list, listDepth, stream);
1594
+ const $sideEffect = $__item.layerPlan.latestSideEffectStep;
1595
+ try {
1596
+ let $item;
1597
+ if ((0, step_js_1.isListCapableStep)($list)) {
1598
+ $item = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)($__item.layerPlan, $__item.polymorphicPaths, listItemPlanningPath, $list.listItem, $list, $__item);
1599
+ }
1600
+ else {
1601
+ $item = $__item;
1602
+ }
1603
+ this.queueNextLayer(this.planIntoOutputPlan, {
1604
+ outputPlan: listOutputPlan,
1605
+ path,
1606
+ planningPath: listItemPlanningPath + "^",
1607
+ polymorphicPaths,
1608
+ parentStep: $item,
1609
+ positionType: nullableFieldType.ofType,
1610
+ layerPlan: $item.layerPlan,
1611
+ selections,
1612
+ parentObjectType: null,
1613
+ responseKey: null,
1614
+ locationDetails,
1615
+ resolverEmulation,
1616
+ listDepth: listDepth + 1,
1617
+ streamDetails: null,
1618
+ });
1619
+ }
1620
+ catch (err) {
1621
+ this.handlePlanningError({
1622
+ outputPlan: listOutputPlan,
1623
+ layerPlan: $__item.layerPlan,
1624
+ objectType: null,
1625
+ responseKey: null,
1626
+ positionType: nullableFieldType.ofType,
1627
+ locationDetails,
1628
+ err,
1629
+ });
1630
+ }
1631
+ finally {
1632
+ $__item.layerPlan.latestSideEffectStep = $sideEffect;
1633
+ }
1634
+ }
1635
+ handlePlanningError(details) {
1636
+ const { outputPlan, layerPlan, objectType, responseKey, positionType, locationDetails, err, } = details;
1637
+ const step = this.withRootLayerPlan(() => (0, index_js_1.error)(err));
1638
+ const nullableFieldType = getNullableType(positionType);
1639
+ const isNonNull = nullableFieldType !== positionType;
1640
+ outputPlan.addChild(objectType, responseKey, {
1641
+ type: "outputPlan",
1642
+ outputPlan: new OutputPlan_js_1.OutputPlan(layerPlan, step, OUTPUT_PLAN_TYPE_NULL, locationDetails),
1643
+ isNonNull,
1644
+ locationDetails,
1645
+ });
1646
+ }
1647
+ /** @internal */
1648
+ polymorphicResolveType(details) {
1649
+ const { outputPlan, path, planningPath: polymorphicResolvePlanningPath, polymorphicPaths, layerPlan, selections, allPossibleObjectTypes, locationDetails, isNonNull, resolverEmulation, stepForType, } = details;
1650
+ if (!stepForType) {
1651
+ throw new Error("GrafastInternalError<6afce944-f963-470f-8575-0d0420b4c838>: planForType wasn't set!");
1652
+ }
1653
+ if (outputPlan.type.mode !== "polymorphic") {
1654
+ throw new Error(`GrafastInternalError<21de2f87-45c9-42c6-a1a3-c2c0f6e72ccb>: expected a polymorphic output plan`);
1655
+ }
1656
+ const polymorphicOutputPlan = outputPlan;
1657
+ /*
1658
+ * Now we need to loop through each type and plan it.
1659
+ */
1660
+ const polyBases = [...(polymorphicPaths ?? [""])];
1661
+ const polymorphicPlanningPath = polymorphicResolvePlanningPath + "!";
1662
+ for (const type of allPossibleObjectTypes) {
1663
+ const $sideEffect = layerPlan.latestSideEffectStep;
1664
+ try {
1665
+ // Bit of a hack, but saves passing it around through all the arguments
1666
+ const newPolymorphicPaths = new Set();
1667
+ for (const polyBase of polyBases) {
1668
+ const newPolymorphicPath = `${polyBase}>${type.name}`;
1669
+ //resolveTypeLayerPlan.reason.polymorphicPaths.add(newPolymorphicPath);
1670
+ newPolymorphicPaths.add(newPolymorphicPath);
1671
+ }
1672
+ const $root = stepForType.get(type);
1673
+ try {
1674
+ if ($root == null) {
1675
+ throw new Error(`${details.positionType}'s planType().planForType(${type}) returned ${$root}`);
1105
1676
  }
1106
- finally {
1107
- polymorphicLayerPlan.latestSideEffectStep = $sideEffect;
1677
+ else if (!($root instanceof index_js_1.Step)) {
1678
+ throw $root;
1108
1679
  }
1109
1680
  }
1681
+ catch (err) {
1682
+ this.handlePlanningError({
1683
+ outputPlan: polymorphicOutputPlan,
1684
+ layerPlan: layerPlan,
1685
+ objectType: type,
1686
+ responseKey: null,
1687
+ positionType: type,
1688
+ locationDetails,
1689
+ err,
1690
+ });
1691
+ return;
1692
+ }
1693
+ // find all selections compatible with `type`
1694
+ const fieldNodes = (0, graphqlMergeSelectionSets_js_1.fieldSelectionsForType)(this, type, selections);
1695
+ this.queueNextLayer(this.polymorphicPlanObjectType, {
1696
+ outputPlan: polymorphicOutputPlan,
1697
+ path,
1698
+ planningPath: polymorphicPlanningPath,
1699
+ polymorphicPaths: newPolymorphicPaths,
1700
+ parentStep: $root,
1701
+ positionType: type,
1702
+ layerPlan,
1703
+ fieldNodes,
1704
+ locationDetails,
1705
+ isNonNull,
1706
+ resolverEmulation,
1707
+ });
1708
+ }
1709
+ finally {
1710
+ layerPlan.latestSideEffectStep = $sideEffect;
1110
1711
  }
1111
1712
  }
1112
1713
  }
1113
- getPolymorphicLayerPlan(parentLayerPlan, path, $step, allPossibleObjectTypes) {
1114
- // OPTIMIZE: I added the $step.id to pathString to fix a planning issue; but maybe we can do this without branching?
1115
- // https://github.com/benjie/crystal/issues/109
1116
- const pathString = `${path.join("|")}!${$step.id}`;
1117
- const polymorphicLayerPlanByPath = this.polymorphicLayerPlanByPathByLayerPlan.get(parentLayerPlan) ??
1118
- new Map();
1119
- if (polymorphicLayerPlanByPath.size === 0) {
1120
- this.polymorphicLayerPlanByPathByLayerPlan.set(parentLayerPlan, polymorphicLayerPlanByPath);
1714
+ // Before calling this, all the deduplication in the resolveType layer plan
1715
+ // should have taken place, and for each step that survives, a polymorphic
1716
+ // layer plan matching those relevant types should be created, and passed
1717
+ // through here as `polymorphicLayerPlan`.
1718
+ polymorphicPlanObjectType(details) {
1719
+ const { outputPlan, path, planningPath: polymorphicPlanningPath, polymorphicPaths: newPolymorphicPaths, parentStep: $root, positionType: type, layerPlan: polymorphicLayerPlan, fieldNodes, locationDetails, isNonNull, resolverEmulation, } = details;
1720
+ if (outputPlan.type.mode !== "polymorphic") {
1721
+ throw new Error(`GrafastInternalError<4c8d9d82-6cb2-4712-aa1e-fdd2173a0760>: expected a polymorphic output plan`);
1722
+ }
1723
+ const polymorphicOutputPlan = outputPlan;
1724
+ if (polymorphicLayerPlan.reason.type !== "polymorphic" &&
1725
+ polymorphicLayerPlan.reason.type !== "polymorphicPartition") {
1726
+ // NOTE: when queued, this method will be queued with a different layer
1727
+ // plan, but `planPending` should go through and convert it to the
1728
+ // relevant polymorphic layer plans for us.
1729
+ throw new Error(`GrafastInternalError<877eaa1c-30c9-4526-ada4-3ccce020ee0e>: expected ${polymorphicLayerPlan} to be a polymorphic or polymorphicPartition layer plan`);
1730
+ }
1731
+ const objectOutputPlan = new OutputPlan_js_1.OutputPlan(polymorphicLayerPlan, $root, {
1732
+ mode: "object",
1733
+ deferLabel: undefined,
1734
+ typeName: type.name,
1735
+ }, locationDetails);
1736
+ this.planSelectionSet({
1737
+ outputPlan: objectOutputPlan,
1738
+ path,
1739
+ planningPath: polymorphicPlanningPath + ".",
1740
+ polymorphicPaths: newPolymorphicPaths,
1741
+ parentStep: $root,
1742
+ positionType: type,
1743
+ layerPlan: polymorphicLayerPlan,
1744
+ selections: fieldNodes,
1745
+ resolverEmulation,
1746
+ });
1747
+ polymorphicOutputPlan.addChild(type, null, {
1748
+ type: "outputPlan",
1749
+ isNonNull,
1750
+ outputPlan: objectOutputPlan,
1751
+ locationDetails,
1752
+ });
1753
+ }
1754
+ batchPlanField(batchPlanFieldDetails) {
1755
+ let b;
1756
+ if (this.planFieldBatch != null) {
1757
+ b = this.planFieldBatch;
1758
+ }
1759
+ else {
1760
+ b = this.planFieldBatch = {
1761
+ complete: false,
1762
+ batch: [],
1763
+ results: [],
1764
+ };
1121
1765
  }
1122
- const prev = polymorphicLayerPlanByPath.get(pathString);
1123
- if (prev !== undefined) {
1124
- // NOTE: this is typically hit when you have a polymorphic field inside
1125
- // another polymorphic field - in these cases rather than having the
1126
- // polymorphism multiply out, we can aim for fewer polymorphic buckets
1127
- // keeping the plan simpler (and more efficient).
1128
- const { stepId, layerPlan } = prev;
1129
- const stepByStepId = this.stepTracker.getStepById(stepId);
1130
- const stepBy$stepId = this.stepTracker.getStepById($step.id);
1131
- if (stepByStepId !== stepBy$stepId) {
1132
- 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}))`);
1766
+ const myIndex = b.batch.length;
1767
+ b.batch.push(batchPlanFieldDetails);
1768
+ return () => {
1769
+ if (!b.complete) {
1770
+ this.processPlanField(b);
1133
1771
  }
1134
- for (const t of allPossibleObjectTypes) {
1135
- if (!layerPlan.reason.typeNames.includes(t.name)) {
1136
- // Since we're re-using an existing LayerPlan, we should be careful to
1137
- // ensure none of the previous assumptions have been broken.
1138
- layerPlan.reason.typeNames.push(t.name);
1772
+ const result = b.results[myIndex];
1773
+ if (result.error) {
1774
+ throw result.error;
1775
+ }
1776
+ return result;
1777
+ };
1778
+ }
1779
+ processPlanField(b) {
1780
+ b.complete = true;
1781
+ this.planFieldBatch = null;
1782
+ const groups = new Map();
1783
+ for (let i = 0, l = b.batch.length; i < l; i++) {
1784
+ const batchPlanFieldDetails = b.batch[i];
1785
+ const { typeName, fieldName, layerPlan,
1786
+ // path,
1787
+ polymorphicPaths, planningPath,
1788
+ // planResolver,
1789
+ // applyAfterMode,
1790
+ rawParentStep,
1791
+ // field,
1792
+ trackedArguments, streamDetails, } = batchPlanFieldDetails;
1793
+ const argsSig = Object.entries(trackedArguments)
1794
+ .map(([key, step]) => `${key}:${step.id}`)
1795
+ .join(",");
1796
+ /**
1797
+ * Our tests don't come up with anywhere where rawParentStep.id needs
1798
+ * incorporation into the signature, but I'm feeling paranoid.
1799
+ */
1800
+ const benjiesParanoia = `[${rawParentStep.id}]`;
1801
+ const signature = `${planningPath}@${layerPlan.id}${benjiesParanoia}=${typeName}.${fieldName}(${argsSig})`;
1802
+ let entry = groups.get(signature);
1803
+ if (!entry) {
1804
+ entry = {
1805
+ firstDetails: batchPlanFieldDetails,
1806
+ extraDetails: [{ polymorphicPaths }],
1807
+ streamDetails,
1808
+ indexes: [i],
1809
+ };
1810
+ groups.set(signature, entry);
1811
+ }
1812
+ else {
1813
+ if (index_js_1.isDev) {
1814
+ // Check everything lines up as we would expect
1815
+ for (const key of Object.keys(batchPlanFieldDetails)) {
1816
+ const myVal = batchPlanFieldDetails[key];
1817
+ const first = entry.firstDetails[key];
1818
+ if (myVal === first)
1819
+ continue;
1820
+ switch (key) {
1821
+ case "typeName":
1822
+ throw new Error(`GraphileInternalError<15e09f3f-1a83-48a8-822c-1158b9740ef7>: processPlanField signature failure - mismatch typeName`);
1823
+ case "fieldName":
1824
+ throw new Error(`GraphileInternalError<2999cbf9-7143-4677-9592-0bfc52e12cb0>: processPlanField signature failure - mismatch fieldName`);
1825
+ case "layerPlan":
1826
+ throw new Error(`GraphileInternalError<52786d18-cfda-4ed6-a309-9cb31dcd5f34>: processPlanField signature failure - mismatch layerPlan`);
1827
+ case "path":
1828
+ if (!(0, utils_js_1.arraysMatch)(batchPlanFieldDetails.path, entry.firstDetails.path)) {
1829
+ throw new Error(`GraphileInternalError<cb84829d-ee4c-40cd-a3a6-f53d1029ad83>: processPlanField signature failure - mismatch path`);
1830
+ }
1831
+ break;
1832
+ case "polymorphicPaths":
1833
+ // Explicitly differences in polymorphicPaths are allowed
1834
+ break;
1835
+ case "planningPath":
1836
+ throw new Error(`GraphileInternalError<1b708215-1549-47c3-9cf5-8fd8d7eb9bd0>: processPlanField signature failure - mismatch planningPath`);
1837
+ case "planResolver":
1838
+ throw new Error(`GraphileInternalError<7f14700f-2272-4b6a-a927-7600d934fdf0>: processPlanField signature failure - mismatch planResolver`);
1839
+ case "applyAfterMode":
1840
+ throw new Error(`GraphileInternalError<bbddad4f-68aa-49c7-b82c-09ceebf14a3f>: processPlanField signature failure - mismatch applyAfterMode`);
1841
+ case "rawParentStep":
1842
+ throw new Error(`GraphileInternalError<3306994d-7881-4495-89d1-6c9252840369>: processPlanField signature failure - mismatch rawParentStep`);
1843
+ case "field":
1844
+ throw new Error(`GraphileInternalError<67d2a787-2383-4228-8358-6ea9b3321445>: processPlanField signature failure - mismatch field`);
1845
+ case "trackedArguments":
1846
+ if (!(0, utils_js_1.recordsMatch)(batchPlanFieldDetails.trackedArguments, entry.firstDetails.trackedArguments)) {
1847
+ throw new Error(`GraphileInternalError<ec3f1e44-0ab6-445c-a644-a4d276f4b787>: processPlanField signature failure - mismatch trackedArguments`);
1848
+ }
1849
+ break;
1850
+ case "streamDetails":
1851
+ // If any of them don't stream, turn streaming off
1852
+ if (!streamDetails) {
1853
+ entry.streamDetails = null;
1854
+ }
1855
+ break;
1856
+ default: {
1857
+ const never = key;
1858
+ throw new Error(`GraphileInternalError<3f039c33-0b43-47d8-8da8-57873186cd60>: unexpected key '${never}'`);
1859
+ }
1860
+ }
1861
+ }
1139
1862
  }
1863
+ entry.indexes.push(i);
1864
+ entry.extraDetails.push({ polymorphicPaths });
1865
+ }
1866
+ }
1867
+ // Loop through the groups and resolve the plan ONCE per group.
1868
+ // We don't care about the signature here; that was just for grouping
1869
+ for (const entry of groups.values()) {
1870
+ const planFieldDetails = {
1871
+ ...entry.firstDetails,
1872
+ polymorphicPaths: entry.firstDetails.polymorphicPaths
1873
+ ? new Set([
1874
+ ...entry.firstDetails.polymorphicPaths,
1875
+ ...entry.extraDetails.flatMap((d) => [...d.polymorphicPaths]),
1876
+ ])
1877
+ : null,
1878
+ };
1879
+ let result;
1880
+ try {
1881
+ result = this._realPlanField(planFieldDetails);
1882
+ }
1883
+ catch (error) {
1884
+ result = { error };
1885
+ }
1886
+ for (const i of entry.indexes) {
1887
+ b.results[i] = result;
1140
1888
  }
1141
- return layerPlan;
1142
- }
1143
- else {
1144
- const layerPlan = new LayerPlan_js_1.LayerPlan(this, parentLayerPlan, {
1145
- type: "polymorphic",
1146
- typeNames: allPossibleObjectTypes.map((t) => t.name),
1147
- parentStep: $step,
1148
- polymorphicPaths: new Set(),
1149
- });
1150
- polymorphicLayerPlanByPath.set(pathString, {
1151
- stepId: $step.id,
1152
- layerPlan,
1153
- });
1154
- return layerPlan;
1155
1889
  }
1156
1890
  }
1157
- planField(typeName, fieldName, layerPlan, path, polymorphicPaths, planResolver, applyAfterMode, rawParentStep, field, trackedArguments,
1158
- // If 'true' this is a subscription rather than a stream
1159
- // If 'false' this is a list but it will never stream
1160
- // If 'null' this is neither subscribe field nor list field
1161
- // Otherwise, it's a list field that has the `@stream` directive applied
1162
- streamDetails, deduplicate = true) {
1891
+ _realPlanField(planFieldDetails) {
1892
+ const { typeName, fieldName, layerPlan, path, polymorphicPaths, planningPath, planResolver, applyAfterMode, rawParentStep, field, trackedArguments, streamDetails, } = planFieldDetails;
1163
1893
  const coordinate = `${typeName}.${fieldName}`;
1164
1894
  // The step may have been de-duped whilst sibling steps were planned
1165
1895
  // PERF: this should be handled in the parent?
@@ -1169,7 +1899,7 @@ class OperationPlan {
1169
1899
  if (this.loc !== null)
1170
1900
  this.loc.push(`planField(${path.join(".")})`);
1171
1901
  try {
1172
- let step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, operationPlan_input_js_1.withFieldArgsForArguments, null, this, trackedArguments, field, parentStep, applyAfterMode, coordinate, (fieldArgs) => planResolver(parentStep, fieldArgs, {
1902
+ let step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, planningPath, operationPlan_input_js_1.withFieldArgsForArguments, null, this, trackedArguments, field, parentStep, applyAfterMode, coordinate, (fieldArgs) => planResolver(parentStep, fieldArgs, {
1173
1903
  fieldName,
1174
1904
  field,
1175
1905
  schema: this.schema,
@@ -1180,7 +1910,7 @@ class OperationPlan {
1180
1910
  step =
1181
1911
  step ||
1182
1912
  // `withGlobalLayerPlan(layerPlan, polymorphicPaths, () => constant(null))` but with reduced memory allocation
1183
- (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, constant_js_1.constant, null, null);
1913
+ (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, planningPath, constant_js_1.constant, null, null);
1184
1914
  haltTree = true;
1185
1915
  }
1186
1916
  (0, step_js_1.assertExecutableStep)(step);
@@ -1200,15 +1930,6 @@ class OperationPlan {
1200
1930
  };
1201
1931
  step._stepOptions.walkIterable = true;
1202
1932
  }
1203
- if (deduplicate) {
1204
- // Now that the field has been planned (including arguments, but NOT
1205
- // including selection set) we can deduplicate it to see if any of its
1206
- // peers are identical.
1207
- this.deduplicateSteps();
1208
- // After deduplication, this step may have been substituted; get the
1209
- // updated reference.
1210
- step = this.stepTracker.getStepById(step.id);
1211
- }
1212
1933
  return { step, haltTree };
1213
1934
  }
1214
1935
  catch (e) {
@@ -1231,7 +1952,7 @@ class OperationPlan {
1231
1952
  console.error(`Cleanup error occurred whilst trying to recover from field planning error: ${e2.stack}`);
1232
1953
  throw e;
1233
1954
  }
1234
- const step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, index_js_1.error, null, e);
1955
+ const step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, planningPath, index_js_1.error, null, e);
1235
1956
  const haltTree = true;
1236
1957
  // PERF: consider deleting all steps that were allocated during this. For
1237
1958
  // now we'll just rely on tree-shaking.
@@ -1264,18 +1985,14 @@ class OperationPlan {
1264
1985
  const argumentPlan = (0, input_js_1.inputStep)(this, argumentType, argumentValue?.value, defaultValue);
1265
1986
  trackedArgumentValues[argumentName] = argumentPlan;
1266
1987
  }
1267
- return {
1268
- get(name) {
1269
- return trackedArgumentValues[name];
1270
- },
1271
- };
1988
+ return trackedArgumentValues;
1272
1989
  }
1273
1990
  /**
1274
1991
  * Sets up tracking for the given value (variableValues, context, rootValue).
1275
1992
  */
1276
1993
  track(value, constraints, variableDefinitions) {
1277
- const valueStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, newValueStepCallback, null, variableDefinitions != null);
1278
- const trackedObjectStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => new index_js_1.__TrackedValueStep(value, valueStep, constraints, [], variableDefinitions != null, variableDefinitions));
1994
+ const valueStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, "", newValueStepCallback, null, variableDefinitions != null);
1995
+ const trackedObjectStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, "", () => new index_js_1.__TrackedValueStep(value, valueStep, constraints, [], variableDefinitions != null, variableDefinitions));
1279
1996
  return [valueStep, trackedObjectStep];
1280
1997
  }
1281
1998
  /**
@@ -1322,7 +2039,7 @@ class OperationPlan {
1322
2039
  }
1323
2040
  }
1324
2041
  }
1325
- const subroutineLayerPlan = step[interfaces_js_1.$$subroutine];
2042
+ const subroutineLayerPlan = step[constants_js_1.$$subroutine];
1326
2043
  if (subroutineLayerPlan !== null) {
1327
2044
  const $root = subroutineLayerPlan.rootStep;
1328
2045
  if ($root) {
@@ -1344,7 +2061,7 @@ class OperationPlan {
1344
2061
  }
1345
2062
  let replacementStep = step;
1346
2063
  try {
1347
- replacementStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, callback, this, step);
2064
+ replacementStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, null, callback, this, step);
1348
2065
  }
1349
2066
  catch (e) {
1350
2067
  console.error(`Error occurred during ${actionDescription}; whilst processing ${step} in ${order} mode an error occurred:`, e);
@@ -1362,7 +2079,9 @@ class OperationPlan {
1362
2079
  if (replacementStep !== step) {
1363
2080
  this.replaceStep(step, replacementStep);
1364
2081
  }
1365
- this.deduplicateSteps();
2082
+ if (actionDescription !== "deduplicate") {
2083
+ this.deduplicateSteps();
2084
+ }
1366
2085
  }
1367
2086
  return replacementStep;
1368
2087
  }
@@ -1421,21 +2140,18 @@ class OperationPlan {
1421
2140
  // Plans with side effects have no peers.
1422
2141
  return EMPTY_ARRAY;
1423
2142
  }
1424
- if (step._stepOptions.stream) {
1425
- // Streams have no peers - we cannot reference the stream more
1426
- // than once (and we aim to not cache the stream because we want its
1427
- // entries to be garbage collected).
1428
- //
1429
- // HOWEVER! There may be lifecycle parts that need to be called... So
1430
- // call the function with an empty array; ignore the result.
1431
- return EMPTY_ARRAY;
1432
- }
1433
2143
  // NOTE: Streams have no peers - we cannot reference the stream more than
1434
2144
  // once (and we aim to not cache the stream because we want its entries to
1435
2145
  // be garbage collected) - however if we're already fetching the list then
1436
2146
  // we shouldn't fetch it again via stream... We should deduplicate a stream
1437
2147
  // to return a non-stream.
1438
2148
  if (step._stepOptions.stream != null) {
2149
+ // Streams have no peers - we cannot reference the stream more
2150
+ // than once (and we aim to not cache the stream because we want its
2151
+ // entries to be garbage collected).
2152
+ //
2153
+ // HOWEVER! There may be lifecycle parts that need to be called... So
2154
+ // call the function with an empty array; ignore the result.
1439
2155
  // TODO: remove this if block when we implement the new stream/defer -
1440
2156
  // deduplicating a stream should be fine. (Not subscriptions though - may
1441
2157
  // need a check for that!)
@@ -1446,7 +2162,7 @@ class OperationPlan {
1446
2162
  return EMPTY_ARRAY;
1447
2163
  }
1448
2164
  const sstep = (0, utils_js_1.sudo)(step);
1449
- const { dependencies: deps, dependencyForbiddenFlags: flags, dependencyOnReject: onReject, layerPlan: layerPlan, constructor: stepConstructor, peerKey, } = sstep;
2165
+ const { dependencies: deps, dependencyForbiddenFlags: flags, dependencyOnReject: onReject, dependencyDataOnly: dataOnly, layerPlan: layerPlan, constructor: stepConstructor, peerKey, } = sstep;
1450
2166
  // const streamInitialCount = sstep._stepOptions.stream?.initialCount;
1451
2167
  const dependencyCount = deps.length;
1452
2168
  if (dependencyCount === 0) {
@@ -1455,7 +2171,7 @@ class OperationPlan {
1455
2171
  for (const possiblyPeer of stepsWithNoDependencies) {
1456
2172
  if (possiblyPeer !== step &&
1457
2173
  !possiblyPeer.hasSideEffects &&
1458
- possiblyPeer.layerPlan === layerPlan &&
2174
+ isPeerLayerPlan(possiblyPeer.layerPlan, layerPlan) &&
1459
2175
  possiblyPeer._stepOptions.stream == null &&
1460
2176
  possiblyPeer.peerKey === peerKey
1461
2177
  // && possiblyPeer._stepOptions.stream?.initialCount === streamInitialCount
@@ -1472,8 +2188,9 @@ class OperationPlan {
1472
2188
  }
1473
2189
  else if (dependencyCount === 1) {
1474
2190
  // Optimized form for steps that have one dependency (extremely common!)
2191
+ const dependencyIndex = 0;
1475
2192
  const { ancestry, deferBoundaryDepth } = layerPlan;
1476
- const dep = deps[0];
2193
+ const dep = deps[dependencyIndex];
1477
2194
  const dl = dep.dependents.length;
1478
2195
  if (dl === 1) {
1479
2196
  // We're the only dependent; therefore we have no peers (since peers
@@ -1483,7 +2200,7 @@ class OperationPlan {
1483
2200
  const minDepth = Math.max(deferBoundaryDepth, dep.layerPlan.depth);
1484
2201
  let allPeers = null;
1485
2202
  for (const { dependencyIndex: peerDependencyIndex, step: rawPossiblyPeer, } of dep.dependents) {
1486
- if (peerDependencyIndex !== 0 ||
2203
+ if (peerDependencyIndex !== dependencyIndex ||
1487
2204
  rawPossiblyPeer === step ||
1488
2205
  rawPossiblyPeer.hasSideEffects ||
1489
2206
  rawPossiblyPeer._stepOptions.stream != null ||
@@ -1494,12 +2211,13 @@ class OperationPlan {
1494
2211
  continue;
1495
2212
  }
1496
2213
  const possiblyPeer = (0, utils_js_1.sudo)(rawPossiblyPeer);
1497
- const { layerPlan: peerLayerPlan, dependencyForbiddenFlags: peerFlags, dependencyOnReject: peerOnReject, } = possiblyPeer;
2214
+ const { layerPlan: peerLayerPlan, dependencyForbiddenFlags: peerFlags, dependencyOnReject: peerOnReject, dependencyDataOnly: peerDataOnly, } = possiblyPeer;
1498
2215
  if (peerLayerPlan.depth >= minDepth &&
1499
2216
  possiblyPeer.dependencies.length === dependencyCount &&
1500
- peerLayerPlan === ancestry[peerLayerPlan.depth] &&
1501
- peerFlags[0] === flags[0] &&
1502
- peerOnReject[0] === onReject[0]) {
2217
+ isPeerLayerPlan(peerLayerPlan, ancestry[peerLayerPlan.depth]) &&
2218
+ peerFlags[dependencyIndex] === flags[dependencyIndex] &&
2219
+ peerOnReject[dependencyIndex] === onReject[dependencyIndex] &&
2220
+ peerDataOnly[dependencyIndex] === dataOnly[dependencyIndex]) {
1503
2221
  if (allPeers === null) {
1504
2222
  allPeers = [possiblyPeer];
1505
2223
  }
@@ -1548,11 +2266,12 @@ class OperationPlan {
1548
2266
  continue;
1549
2267
  }
1550
2268
  const possiblyPeer = (0, utils_js_1.sudo)(rawPossiblyPeer);
1551
- const { layerPlan: peerLayerPlan, dependencyForbiddenFlags: peerFlags, dependencyOnReject: peerOnReject, dependencies: peerDependencies, } = possiblyPeer;
2269
+ const { layerPlan: peerLayerPlan, dependencyForbiddenFlags: peerFlags, dependencyOnReject: peerOnReject, dependencyDataOnly: peerDataOnly, dependencies: peerDependencies, } = possiblyPeer;
1552
2270
  if (peerDependencies.length === dependencyCount &&
1553
- peerLayerPlan === ancestry[peerLayerPlan.depth] &&
1554
- peerFlags[0] === flags[0] &&
1555
- peerOnReject[0] === onReject[0]) {
2271
+ isPeerLayerPlan(peerLayerPlan, ancestry[peerLayerPlan.depth]) &&
2272
+ peerFlags[dependencyIndex] === flags[dependencyIndex] &&
2273
+ peerOnReject[dependencyIndex] === onReject[dependencyIndex] &&
2274
+ peerDataOnly[dependencyIndex] === dataOnly[dependencyIndex]) {
1556
2275
  possiblePeers.push(possiblyPeer);
1557
2276
  }
1558
2277
  }
@@ -1569,10 +2288,17 @@ class OperationPlan {
1569
2288
  outerloop: for (const possiblyPeer of possiblePeers) {
1570
2289
  if (possiblyPeer.layerPlan.depth < minDepth)
1571
2290
  continue;
2291
+ const speer = (0, utils_js_1.sudo)(possiblyPeer);
2292
+ // PERF: I'm not sure we strictly require this.
2293
+ if (!(0, utils_js_1.arraysMatch)(sstep._refs, speer._refs, (refA, refB) => refA === refB ||
2294
+ this.stepTracker.getStepById(refA) ===
2295
+ this.stepTracker.getStepById(refB))) {
2296
+ continue;
2297
+ }
1572
2298
  // We know the final dependency matches and the dependency count
1573
2299
  // matches - check the other dependencies match.
1574
2300
  for (let i = 0; i < dependencyCount - 1; i++) {
1575
- if (deps[i] !== (0, utils_js_1.sudo)(possiblyPeer).dependencies[i]) {
2301
+ if (deps[i] !== speer.dependencies[i]) {
1576
2302
  continue outerloop;
1577
2303
  }
1578
2304
  }
@@ -1593,7 +2319,7 @@ class OperationPlan {
1593
2319
  if (isPrepopulatedStep(step)) {
1594
2320
  return true;
1595
2321
  }
1596
- if (step[interfaces_js_1.$$subroutine] !== null) {
2322
+ if (step[constants_js_1.$$subroutine] !== null) {
1597
2323
  // Don't hoist steps that are the parent of a subroutine
1598
2324
  // PERF: we _should_ be able to hoist, but care must be taken. Currently it causes test failures.
1599
2325
  return true;
@@ -1620,15 +2346,19 @@ class OperationPlan {
1620
2346
  return true;
1621
2347
  }
1622
2348
  */
1623
- if (step.layerPlan.parentLayerPlan?.reason.type === "mutationField") {
2349
+ if (step.layerPlan.reason.type === "root") {
2350
+ // There is no higher layerPlan
2351
+ return;
2352
+ }
2353
+ else if (step.layerPlan.reason.type === "combined") {
2354
+ // Cannot hoist out of a combination
2355
+ return;
2356
+ }
2357
+ else if (step.layerPlan.reason.parentLayerPlan.reason.type === "mutationField") {
1624
2358
  // Never hoist into a mutation layer
1625
2359
  return;
1626
2360
  }
1627
2361
  switch (step.layerPlan.reason.type) {
1628
- case "root": {
1629
- // There is no higher layerPlan
1630
- return;
1631
- }
1632
2362
  case "subscription":
1633
2363
  case "defer": {
1634
2364
  // Should be deferred, don't evaluate early (unless it's cheap to do so)
@@ -1641,23 +2371,28 @@ class OperationPlan {
1641
2371
  }
1642
2372
  }
1643
2373
  case "polymorphic": {
1644
- // May only need to be evaluated for certain types, so avoid hoisting anything expensive.
1645
- if (step.isSyncAndSafe &&
2374
+ // Only hoist steps that are executed for all the types
2375
+ if (step.polymorphicPaths === null ||
1646
2376
  step.polymorphicPaths.size ===
1647
2377
  step.layerPlan.reason.polymorphicPaths.size) {
1648
- // It's cheap and covers all types, try and hoist it.
1649
- // NOTE: I have concerns about whether this is safe or not, but I
1650
- // have not been able to come up with a counterexample that is
1651
- // unsafe. Should we do so, we should remove this.
2378
+ // It covers all types, try and hoist it.
1652
2379
  break;
1653
2380
  }
1654
2381
  else if (step instanceof index_js_1.__FlagStep) {
2382
+ // Flag step is cheap, try and hoist
1655
2383
  break;
1656
2384
  }
1657
2385
  else {
1658
2386
  return;
1659
2387
  }
1660
2388
  }
2389
+ case "polymorphicPartition": {
2390
+ // Don't want to hoist out of a polymorphic partition
2391
+ // TODO: revisit this, e.g. global unary steps should be hoistable? But
2392
+ // we should only do that if they're cheap, we don't want to run
2393
+ // expensive stuff at a higher level if only certain types need it.
2394
+ return;
2395
+ }
1661
2396
  case "subroutine": {
1662
2397
  // Should be safe to hoist.
1663
2398
  break;
@@ -1712,51 +2447,31 @@ class OperationPlan {
1712
2447
  return;
1713
2448
  }
1714
2449
  // All our checks passed, hoist it.
1715
- assert.ok(step.layerPlan.parentLayerPlan !== null, "GrafastInternalError<55c8940f-e8ac-4985-8b34-96fc6f81d62d>: A non-root layer plan had no parent?!");
1716
2450
  // 1: adjust polymorphicPaths to fit new layerPlan
1717
2451
  if (step.layerPlan.reason.type === "polymorphic") {
1718
2452
  // PERF: this is cacheable
1719
- /** The closest ancestor layer plan that is polymorphic */
1720
- let ancestor = step.layerPlan;
1721
- while ((ancestor = ancestor.parentLayerPlan)) {
1722
- if (ancestor.reason.type === "polymorphic") {
2453
+ let newPolyPaths = new Set();
2454
+ for (const polyPath of step.polymorphicPaths) {
2455
+ const i = polyPath.lastIndexOf(">");
2456
+ if (i > 0) {
2457
+ const newPath = polyPath.slice(0, i);
2458
+ newPolyPaths.add(newPath);
2459
+ }
2460
+ else if (i === 0) {
2461
+ newPolyPaths = null;
1723
2462
  break;
1724
2463
  }
1725
- }
1726
- const parentPolymorphicPaths = ancestor
1727
- ? ancestor.reason
1728
- .polymorphicPaths
1729
- : exports.POLYMORPHIC_ROOT_PATHS;
1730
- const myPaths = [...step.polymorphicPaths];
1731
- if (parentPolymorphicPaths?.has(myPaths[0])) {
1732
- // All the others must be valid too
1733
- }
1734
- else if (parentPolymorphicPaths === null) {
1735
- step.polymorphicPaths = null;
1736
- }
1737
- else {
1738
- const layerPaths = [...step.layerPlan.reason.polymorphicPaths];
1739
- const newPaths = new Set();
1740
- for (const path of parentPolymorphicPaths) {
1741
- const prefix = path + ">";
1742
- const matches = myPaths.filter((p) => p.startsWith(prefix));
1743
- const layerMatches = layerPaths.filter((p) => p.startsWith(prefix));
1744
- if (matches.length !== layerMatches.length) {
1745
- // Can't hoist because it's not used for all polymorphicPaths of this type
1746
- return;
1747
- }
1748
- else if (matches.length > 0) {
1749
- newPaths.add(path);
1750
- }
2464
+ else {
2465
+ throw new Error(`GrafastInternalError<42f1c7e7-e544-46d2-81b3-00db11eb5def>: invalid poly path ${polyPath}`);
1751
2466
  }
1752
- step.polymorphicPaths = newPaths;
1753
2467
  }
2468
+ step.polymorphicPaths = newPolyPaths;
1754
2469
  }
1755
2470
  const $subroutine = step.layerPlan.reason.type === "subroutine"
1756
2471
  ? step.layerPlan.reason.parentStep
1757
2472
  : null;
1758
2473
  // 2: move it up a layer
1759
- this.stepTracker.moveStepToLayerPlan(step, step.layerPlan.parentLayerPlan);
2474
+ this.stepTracker.moveStepToLayerPlan(step, step.layerPlan.reason.parentLayerPlan);
1760
2475
  // 3: if it's was in a subroutine, the subroutine parent plan needs to list it as a dependency
1761
2476
  if ($subroutine) {
1762
2477
  // Naughty naughty
@@ -1783,12 +2498,16 @@ class OperationPlan {
1783
2498
  case "subscription":
1784
2499
  case "defer":
1785
2500
  case "polymorphic":
2501
+ case "polymorphicPartition":
1786
2502
  case "subroutine":
1787
2503
  case "nullableBoundary":
1788
2504
  case "listItem": {
1789
2505
  // Fine to push lower
1790
2506
  break;
1791
2507
  }
2508
+ case "combined": {
2509
+ return step;
2510
+ }
1792
2511
  case "mutationField": {
1793
2512
  // NOTE: It's the user's responsibility to ensure that steps that have
1794
2513
  // side effects are marked as such via `step.hasSideEffects = true`.
@@ -1830,11 +2549,24 @@ class OperationPlan {
1830
2549
  const layerPlansByParent = this.stepTracker.layerPlansByParentStep.get(step);
1831
2550
  if (layerPlansByParent !== undefined) {
1832
2551
  for (const layerPlan of layerPlansByParent) {
1833
- if (layerPlan.parentLayerPlan === step.layerPlan) {
2552
+ if (layerPlan.reason.parentLayerPlan === step.layerPlan) {
1834
2553
  return step;
1835
2554
  }
1836
2555
  else {
1837
- dependentLayerPlans.add(layerPlan.parentLayerPlan);
2556
+ dependentLayerPlans.add(layerPlan.reason.parentLayerPlan);
2557
+ }
2558
+ }
2559
+ }
2560
+ const layerPlansByDependent = this.stepTracker.layerPlansByDependentStep.get(step);
2561
+ if (layerPlansByDependent !== undefined) {
2562
+ for (const layerPlan of layerPlansByDependent) {
2563
+ for (const parentLayerPlan of layerPlan.reason.parentLayerPlans) {
2564
+ if (parentLayerPlan === step.layerPlan) {
2565
+ return step;
2566
+ }
2567
+ else if ((0, utils_js_1.layerPlanHeirarchyContains)(parentLayerPlan, step.layerPlan)) {
2568
+ dependentLayerPlans.add(parentLayerPlan);
2569
+ }
1838
2570
  }
1839
2571
  }
1840
2572
  }
@@ -1858,28 +2590,30 @@ class OperationPlan {
1858
2590
  const paths = [];
1859
2591
  let minPathLength = Infinity;
1860
2592
  for (const dependentLayerPlan of dependentLayerPlans) {
1861
- let lp = dependentLayerPlan;
1862
- const path = [lp];
1863
- while (lp.parentLayerPlan != step.layerPlan) {
1864
- const parent = lp.parentLayerPlan;
1865
- if (!parent) {
1866
- throw new Error(`GrafastInternalError<64c07427-4fe2-43c4-9858-272d33bee0b8>: invalid layer plan heirarchy`);
1867
- }
1868
- lp = parent;
1869
- path.push(lp);
2593
+ const lp = dependentLayerPlan;
2594
+ const childPaths = (0, utils_js_1.pathsFromAncestorToTargetLayerPlan)(step.layerPlan, lp);
2595
+ if (childPaths.length === 0) {
2596
+ throw new Error(`GrafastInternalError<64c07427-4fe2-43c4-9858-272d33bee0b8>: invalid layer plan heirarchy; step ${step} cannot be found from ${dependentLayerPlan}`);
2597
+ }
2598
+ for (const path of childPaths) {
2599
+ paths.push(path);
2600
+ minPathLength = Math.min(path.length, minPathLength);
1870
2601
  }
1871
- paths.push(path);
1872
- minPathLength = Math.min(path.length, minPathLength);
1873
2602
  }
1874
2603
  const dependentLayerPlanCount = dependentLayerPlans.size;
1875
2604
  let deepest = step.layerPlan;
1876
- outerloop: for (let i = 0; i < minPathLength; i++) {
2605
+ outerloop: for (let i = minPathLength - 1; i >= 0; i--) {
1877
2606
  const expected = paths[0][i];
1878
2607
  if (expected.reason.type === "polymorphic") {
1879
2608
  // PERF: reconsider
1880
2609
  // Let's not pass polymorphic boundaries for now
1881
2610
  break;
1882
2611
  }
2612
+ if (expected.reason.type === "polymorphicPartition") {
2613
+ // PERF: reconsider
2614
+ // Let's not pass polymorphic boundaries for now
2615
+ break;
2616
+ }
1883
2617
  if (expected.reason.type === "subroutine") {
1884
2618
  // PERF: reconsider
1885
2619
  // Let's not pass subroutine boundaries for now
@@ -1974,10 +2708,36 @@ class OperationPlan {
1974
2708
  const winner = stepsAtMinDepth[0];
1975
2709
  if (winner.polymorphicPaths !== null) {
1976
2710
  const polymorphicPaths = new Set();
1977
- for (const s of stepsAtMinDepth) {
1978
- for (const p of s.polymorphicPaths) {
1979
- polymorphicPaths.add(p);
2711
+ // If the paths don't match up we must be in a polymorphicPartition and
2712
+ // need to move the winner out of it
2713
+ tryAgain: for (let attempts = 0; attempts < 2; attempts++) {
2714
+ const layerPolymorphicPaths = polymorphicPathsForLayer(winner.layerPlan);
2715
+ // PERF: this string trimming is hideous
2716
+ for (const s of [step, ...equivalentSteps]) {
2717
+ for (const p of s.polymorphicPaths) {
2718
+ let trimmed = p;
2719
+ let i;
2720
+ while (!layerPolymorphicPaths.has(trimmed) &&
2721
+ (i = trimmed.lastIndexOf(">")) >= 0) {
2722
+ if (i === 0) {
2723
+ const reason = winner.layerPlan.reason;
2724
+ if (reason.type === "polymorphicPartition") {
2725
+ // Hoist winner into the polymorphic layer plan, and restart
2726
+ // the loop
2727
+ this.stepTracker.moveStepToLayerPlan(winner, reason.parentLayerPlan);
2728
+ polymorphicPaths.clear();
2729
+ continue tryAgain;
2730
+ }
2731
+ else {
2732
+ throw new Error(`GrafastInternalError<93da1006-3af9-44dd-a54b-5bf6fe3e791c>: ${s} has polymorphic paths ${[...(s.polymorphicPaths ?? [])]}; but ${p} is not in ${[...layerPolymorphicPaths]}.`);
2733
+ }
2734
+ }
2735
+ trimmed = trimmed.slice(0, i);
2736
+ }
2737
+ polymorphicPaths.add(trimmed);
2738
+ }
1980
2739
  }
2740
+ break;
1981
2741
  }
1982
2742
  winner.polymorphicPaths = polymorphicPaths;
1983
2743
  }
@@ -2041,7 +2801,8 @@ class OperationPlan {
2041
2801
  this.deduplicateStepsProcess(processed, start, dep);
2042
2802
  }
2043
2803
  }
2044
- (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, this.phase === "plan" ? this.deduplicateStep : this.hoistAndDeduplicate, this, step);
2804
+ (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, null, // TODO: can we get the operation path when phase === "plan"?
2805
+ this.phase === "plan" ? this.deduplicateStep : this.hoistAndDeduplicate, this, step);
2045
2806
  }
2046
2807
  /**
2047
2808
  * Gives us a chance to replace nearly-duplicate plans with other existing
@@ -2054,12 +2815,17 @@ class OperationPlan {
2054
2815
  * former SELECT additional fields, then transform the results back to what
2055
2816
  * our child plans would be expecting.
2056
2817
  */
2057
- deduplicateSteps(depth = 0) {
2818
+ deduplicateSteps() {
2058
2819
  const start = this.stepTracker.nextStepIdToDeduplicate;
2059
- const end = this.stepTracker.stepCount;
2060
- if (end === start) {
2820
+ if (start === this.stepTracker.stepCount) {
2821
+ // No action necessary since there are no new steps
2061
2822
  return;
2062
2823
  }
2824
+ // Then we deduplicate
2825
+ this._deduplicateStepsInner(start, 0);
2826
+ }
2827
+ _deduplicateStepsInner(start, depth) {
2828
+ const end = this.stepTracker.stepCount;
2063
2829
  const processed = new Set();
2064
2830
  for (let i = start; i < end; i++) {
2065
2831
  const step = this.stepTracker.stepById[i];
@@ -2080,7 +2846,7 @@ class OperationPlan {
2080
2846
  .map((s) => String(s))
2081
2847
  .join(", ")}.`);
2082
2848
  }
2083
- return this.deduplicateSteps(depth + 1);
2849
+ return this._deduplicateStepsInner(end, depth + 1);
2084
2850
  }
2085
2851
  }
2086
2852
  hoistAndDeduplicate(step) {
@@ -2204,7 +2970,7 @@ class OperationPlan {
2204
2970
  for (const dependent of $flag.dependents) {
2205
2971
  const { step, dependencyIndex } = dependent;
2206
2972
  const $dependent = (0, utils_js_1.sudo)(step);
2207
- const inlineDetails = $flag.inline($dependent.getDepOptions(dependencyIndex));
2973
+ const inlineDetails = $flag.inline($dependent._getDepOptions(dependencyIndex));
2208
2974
  if (inlineDetails === null) {
2209
2975
  continue flagLoop;
2210
2976
  }
@@ -2217,11 +2983,13 @@ class OperationPlan {
2217
2983
  }
2218
2984
  // All of them pass the check, let's inline them
2219
2985
  for (const todo of toInline) {
2220
- const { $dependent, dependencyIndex, inlineDetails: { onReject, acceptFlags = interfaces_js_1.DEFAULT_ACCEPT_FLAGS }, } = todo;
2986
+ const { $dependent, dependencyIndex, inlineDetails: { onReject, acceptFlags = constants_js_1.DEFAULT_ACCEPT_FLAGS, dataOnly = false, }, } = todo;
2221
2987
  (0, utils_js_1.writeableArray)($dependent.dependencyOnReject)[dependencyIndex] =
2222
2988
  onReject;
2989
+ (0, utils_js_1.writeableArray)($dependent.dependencyDataOnly)[dependencyIndex] =
2990
+ dataOnly;
2223
2991
  (0, utils_js_1.writeableArray)($dependent.dependencyForbiddenFlags)[dependencyIndex] =
2224
- interfaces_js_1.ALL_FLAGS & ~acceptFlags;
2992
+ constants_js_1.ALL_FLAGS & ~acceptFlags;
2225
2993
  }
2226
2994
  const $flagDep = (0, utils_js_1.sudo)($flag).dependencies[0];
2227
2995
  this.stepTracker.replaceStep($flag, $flagDep);
@@ -2234,6 +3002,11 @@ class OperationPlan {
2234
3002
  for (const step of this.stepTracker.activeSteps) {
2235
3003
  const wasLocked = index_js_1.isDev && (0, lock_js_1.unlock)(step);
2236
3004
  step.finalize();
3005
+ if (step._stepOptions.stream) {
3006
+ // (Potentially) streamed steps aren't sync and safe, we need to
3007
+ // iterate them expensively
3008
+ step.isSyncAndSafe = false;
3009
+ }
2237
3010
  if (wasLocked)
2238
3011
  (0, lock_js_1.lock)(step);
2239
3012
  (0, step_js_1.assertFinalized)(step);
@@ -2250,6 +3023,9 @@ class OperationPlan {
2250
3023
  const ensurePlanAvailableInLayer = (dep, layerPlan) => {
2251
3024
  let currentLayerPlan = layerPlan;
2252
3025
  while (dep.layerPlan !== currentLayerPlan) {
3026
+ if (currentLayerPlan.reason.type === "root") {
3027
+ throw new Error(`GrafastInternalError<7f3ce201-810c-4639-8e69-f44a95221c6d>: reached root whilst ensuring ${dep} is available in ${layerPlan}`);
3028
+ }
2253
3029
  if (currentLayerPlan.copyStepIds.includes(dep.id)) {
2254
3030
  break;
2255
3031
  }
@@ -2261,21 +3037,208 @@ class OperationPlan {
2261
3037
  targetStep._isUnaryLocked = true;
2262
3038
  }
2263
3039
  currentLayerPlan.copyStepIds.push(dep.id);
2264
- currentLayerPlan = currentLayerPlan.parentLayerPlan;
3040
+ if (currentLayerPlan.reason.type === "combined") {
3041
+ const prev = currentLayerPlan;
3042
+ currentLayerPlan = null;
3043
+ // Figure out which of our parent layer plans contains the step
3044
+ for (const parentLayerPlan of prev.reason.parentLayerPlans) {
3045
+ if (dep.layerPlan === parentLayerPlan) {
3046
+ currentLayerPlan = parentLayerPlan;
3047
+ break;
3048
+ }
3049
+ else if ((0, utils_js_1.layerPlanHeirarchyContains)(parentLayerPlan, dep.layerPlan)) {
3050
+ ensurePlanAvailableInLayer(dep, parentLayerPlan);
3051
+ }
3052
+ }
3053
+ if (currentLayerPlan == null) {
3054
+ return;
3055
+ }
3056
+ }
3057
+ else {
3058
+ currentLayerPlan = currentLayerPlan.reason.parentLayerPlan;
3059
+ }
2265
3060
  if (!currentLayerPlan) {
2266
3061
  throw new Error(`GrafastInternalError<8c1640b9-fa3c-440d-99e5-7693d0d7e5d1>: could not find layer plan for '${dep}' in chain from layer plan ${layerPlan}`);
2267
3062
  }
2268
3063
  }
2269
3064
  };
3065
+ // Track exactly which steps are in which layer plans
3066
+ const phaseTransitionLayerPlans = new Set();
3067
+ const combinedLayerPlans = new Set();
3068
+ const childrenByLayerPlan = new Map();
3069
+ const addParent = (lp, parent) => {
3070
+ let list = childrenByLayerPlan.get(parent);
3071
+ if (!list) {
3072
+ list = [];
3073
+ childrenByLayerPlan.set(parent, list);
3074
+ }
3075
+ list.push(lp);
3076
+ };
2270
3077
  for (const layerPlan of this.stepTracker.layerPlans) {
2271
3078
  if (!layerPlan) {
2272
3079
  continue;
2273
3080
  }
3081
+ if ((0, utils_js_1.isPhaseTransitionLayerPlan)(layerPlan)) {
3082
+ phaseTransitionLayerPlans.add(layerPlan);
3083
+ }
3084
+ if (layerPlan.reason.type === "combined") {
3085
+ combinedLayerPlans.add(layerPlan);
3086
+ }
2274
3087
  layerPlan.steps = [];
3088
+ switch (layerPlan.reason.type) {
3089
+ case "root":
3090
+ break;
3091
+ case "combined": {
3092
+ for (const parent of layerPlan.reason.parentLayerPlans) {
3093
+ addParent(layerPlan, parent);
3094
+ }
3095
+ break;
3096
+ }
3097
+ default: {
3098
+ addParent(layerPlan, layerPlan.reason.parentLayerPlan);
3099
+ }
3100
+ }
3101
+ }
3102
+ // Go through all the phase transition layer plans and populate their
3103
+ // "outOfBoundsLayerPlanIds". These are "external inputs" to the subgraph
3104
+ // of layer plans starting at the phase transition layer plan. The external
3105
+ // inputs will always be parent nodes of combined layer plans that are
3106
+ // descendents of the phase transition layer plan.
3107
+ for (const phaseTransitionNode of phaseTransitionLayerPlans) {
3108
+ const descendents = getDescendents(phaseTransitionNode, childrenByLayerPlan);
3109
+ for (const combo of combinedLayerPlans) {
3110
+ if (descendents.has(combo)) {
3111
+ for (const parent of combo.reason.parentLayerPlans) {
3112
+ if (!descendents.has(parent)) {
3113
+ phaseTransitionNode.outOfBoundsLayerPlanIds.add(parent.id);
3114
+ }
3115
+ }
3116
+ }
3117
+ }
2275
3118
  }
2276
3119
  for (const step of this.stepTracker.activeSteps) {
2277
3120
  step.layerPlan.steps.push(step);
2278
3121
  }
3122
+ // For each layer plan, track the polymorphic types, and mark the steps in
3123
+ // that layer plan as selective if they don't possess the same number of
3124
+ // poly paths.
3125
+ const polyPathsByLayerPlan = new Map();
3126
+ const processPolymorphicPathsInLayerPlan = (layerPlan) => {
3127
+ const existing = polyPathsByLayerPlan.get(layerPlan);
3128
+ if (existing) {
3129
+ return existing;
3130
+ }
3131
+ const parentPolyPaths = layerPlan.reason.type === "root"
3132
+ ? EMPTY_SET
3133
+ : layerPlan.reason.type === "combined"
3134
+ ? new Set(layerPlan.reason.parentLayerPlans.flatMap((p) => [
3135
+ ...processPolymorphicPathsInLayerPlan(p),
3136
+ ]))
3137
+ : processPolymorphicPathsInLayerPlan(layerPlan.reason.parentLayerPlan);
3138
+ let polyPaths;
3139
+ const reason = layerPlan.reason;
3140
+ switch (reason.type) {
3141
+ case "root":
3142
+ case "nullableBoundary":
3143
+ case "listItem":
3144
+ case "defer":
3145
+ case "subroutine":
3146
+ case "subscription":
3147
+ case "mutationField": {
3148
+ polyPaths = parentPolyPaths;
3149
+ break;
3150
+ }
3151
+ case "combined": {
3152
+ // Resolve redundant combinations now plans have been deduped/optimized
3153
+ for (const combo of layerPlan.combinations) {
3154
+ const { sources } = combo;
3155
+ let newSources = null;
3156
+ const l = sources.length;
3157
+ // Deliberately miss the first (index=0) item
3158
+ for (let i = l - 1; i > 0; i--) {
3159
+ const source = sources[i];
3160
+ const { layerPlanId, stepId } = source;
3161
+ for (let j = i - 1; j >= 0; j--) {
3162
+ const otherSource = sources[j];
3163
+ if (otherSource.layerPlanId === layerPlanId &&
3164
+ otherSource.stepId === stepId) {
3165
+ if (!newSources)
3166
+ newSources = [...sources];
3167
+ // Duplicate! Remove it!
3168
+ newSources.splice(i, 1);
3169
+ break;
3170
+ }
3171
+ }
3172
+ }
3173
+ if (newSources) {
3174
+ combo.sources = newSources;
3175
+ }
3176
+ }
3177
+ // Populate our final polyPaths
3178
+ const newPolyPaths = new Set();
3179
+ for (const lp of reason.parentLayerPlans) {
3180
+ const paths = processPolymorphicPathsInLayerPlan(lp);
3181
+ for (const p of paths) {
3182
+ newPolyPaths.add(p);
3183
+ }
3184
+ }
3185
+ polyPaths = newPolyPaths;
3186
+ // Check that everything is sensible
3187
+ if (index_js_1.isDev) {
3188
+ // Let's check we're being sensible - each poly path should only be
3189
+ // sourceable from one step, and these steps should only have poly
3190
+ // paths that we understand
3191
+ for (const { sources } of layerPlan.combinations) {
3192
+ const overlapCheck = new Map();
3193
+ for (const source of sources) {
3194
+ const { layerPlanId, stepId } = source;
3195
+ const step = this.stepTracker.getStepById(stepId);
3196
+ const paths = step.polymorphicPaths;
3197
+ if (paths === null) {
3198
+ throw new Error(`GrafastInternalError<170ac297-e7b2-4d15-bf3b-1c0c50a0da40>: Step ${step} shouldn't be used in a combo because it's valid in all polymorphic paths`);
3199
+ }
3200
+ for (const p of paths) {
3201
+ if (overlapCheck.has(p)) {
3202
+ const sourceLp = this.stepTracker.layerPlans[layerPlanId];
3203
+ const dup = overlapCheck.get(p);
3204
+ const dupStep = this.stepTracker.getStepById(dup.stepId);
3205
+ const dupLp = this.stepTracker.layerPlans[dup.layerPlanId];
3206
+ throw new Error(`GrafastInternalError<f2d906fe-7f52-4234-a172-42691613f733>: Overlapping path ${p} in ${layerPlan} found; sourced from both ${step} in ${sourceLp} and ${dupStep} in ${dupLp}`);
3207
+ }
3208
+ if (!newPolyPaths.has(p)) {
3209
+ throw new Error(`GrafastInternalError<c697df8f-e194-42f0-9f73-c510bc2e9b74>: path ${p} in ${layerPlan} not expected; sourced from ${step}`);
3210
+ }
3211
+ overlapCheck.set(p, source);
3212
+ }
3213
+ }
3214
+ }
3215
+ }
3216
+ break;
3217
+ }
3218
+ case "polymorphic":
3219
+ case "polymorphicPartition": {
3220
+ polyPaths = reason.polymorphicPaths;
3221
+ break;
3222
+ }
3223
+ default: {
3224
+ const never = reason;
3225
+ throw new Error(`GrafastInternalError<032aef95-97d8-4900-95f7-edc34a5d3703>: unexpected layer plan reason ${(0, inspect_js_1.inspect)(never)}`);
3226
+ }
3227
+ }
3228
+ for (const step of layerPlan.steps) {
3229
+ if (step.polymorphicPaths !== null &&
3230
+ step.polymorphicPaths.size !== polyPaths.size) {
3231
+ step._isSelectiveStep = true;
3232
+ }
3233
+ }
3234
+ polyPathsByLayerPlan.set(layerPlan, polyPaths);
3235
+ return polyPaths;
3236
+ };
3237
+ for (const layerPlan of this.stepTracker.layerPlans) {
3238
+ if (!layerPlan)
3239
+ continue;
3240
+ processPolymorphicPathsInLayerPlan(layerPlan);
3241
+ }
2279
3242
  for (const layerPlan of this.stepTracker.layerPlans) {
2280
3243
  if (!layerPlan) {
2281
3244
  continue;
@@ -2284,7 +3247,25 @@ class OperationPlan {
2284
3247
  const sideEffectSteps = layerPlan.pendingSteps.filter((s) => s.hasSideEffects);
2285
3248
  const pending = new Set(layerPlan.pendingSteps);
2286
3249
  const processed = new Set();
2287
- let latestSideEffectStep = null;
3250
+ const latestSideEffectStepByPolymorphicPath = new Map();
3251
+ const getLatestSideEffectStepFor = (step) => {
3252
+ const polymorphicPaths = [...(step.polymorphicPaths ?? [""])];
3253
+ const latestSideEffectStep = latestSideEffectStepByPolymorphicPath.get(polymorphicPaths[0]);
3254
+ for (let i = 1, l = polymorphicPaths.length; i < l; i++) {
3255
+ const se = latestSideEffectStepByPolymorphicPath.get(polymorphicPaths[i]);
3256
+ if (se !== latestSideEffectStep) {
3257
+ throw new Error(`You shouldn't have side effects in polymorphic positions; ${step} exists in ${polymorphicPaths} but these positions have mixed side effects (${latestSideEffectStep} @ ${polymorphicPaths[0]}, ${se} @ ${polymorphicPaths[i]})`);
3258
+ }
3259
+ }
3260
+ return latestSideEffectStep;
3261
+ };
3262
+ const setLatestSideEffectStep = (step) => {
3263
+ const polymorphicPaths = [...(step.polymorphicPaths ?? [""])];
3264
+ // Store this side effect for use from now on
3265
+ for (let i = 0, l = polymorphicPaths.length; i < l; i++) {
3266
+ latestSideEffectStepByPolymorphicPath.set(polymorphicPaths[i], step);
3267
+ }
3268
+ };
2288
3269
  const processSideEffectPlan = (step) => {
2289
3270
  if (processed.has(step) || isPrepopulatedStep(step)) {
2290
3271
  return;
@@ -2317,12 +3298,13 @@ class OperationPlan {
2317
3298
  for (const dep of rest) {
2318
3299
  processSideEffectPlan(dep);
2319
3300
  }
2320
- if (latestSideEffectStep !== null &&
3301
+ const latestSideEffectStep = getLatestSideEffectStepFor(step);
3302
+ if (latestSideEffectStep !== undefined &&
2321
3303
  !(0, utils_js_1.stepADependsOnStepB)(sstep, latestSideEffectStep)) {
2322
3304
  sstep.implicitSideEffectStep = latestSideEffectStep;
2323
3305
  }
2324
3306
  if (step.hasSideEffects) {
2325
- latestSideEffectStep = step;
3307
+ setLatestSideEffectStep(step);
2326
3308
  }
2327
3309
  const phase = /*#__INLINE__*/ newLayerPlanPhase();
2328
3310
  phase.checkTimeout = true;
@@ -2359,11 +3341,14 @@ class OperationPlan {
2359
3341
  processed.add(step);
2360
3342
  pending.delete(step);
2361
3343
  const sstep = (0, utils_js_1.sudo)(step);
2362
- if (latestSideEffectStep !== null &&
3344
+ const latestSideEffectStep = getLatestSideEffectStepFor(step);
3345
+ if (latestSideEffectStep !== undefined &&
2363
3346
  !(0, utils_js_1.stepADependsOnStepB)(sstep, latestSideEffectStep)) {
2364
3347
  sstep.implicitSideEffectStep = latestSideEffectStep;
2365
3348
  }
2366
- if (step.isSyncAndSafe && (0, step_js_1.isUnbatchedStep)(step)) {
3349
+ if (step.isSyncAndSafe &&
3350
+ (0, step_js_1.isUnbatchedStep)(step) &&
3351
+ isSafeForUnbatched(step)) {
2367
3352
  if (phase.unbatchedSyncAndSafeSteps !== undefined) {
2368
3353
  phase.unbatchedSyncAndSafeSteps.push({
2369
3354
  step,
@@ -2398,7 +3383,8 @@ class OperationPlan {
2398
3383
  processed.add(step);
2399
3384
  pending.delete(step);
2400
3385
  const sstep = (0, utils_js_1.sudo)(step);
2401
- if (latestSideEffectStep !== null &&
3386
+ const latestSideEffectStep = getLatestSideEffectStepFor(step);
3387
+ if (latestSideEffectStep !== undefined &&
2402
3388
  !(0, utils_js_1.stepADependsOnStepB)(sstep, latestSideEffectStep)) {
2403
3389
  sstep.implicitSideEffectStep = latestSideEffectStep;
2404
3390
  }
@@ -2464,7 +3450,15 @@ class OperationPlan {
2464
3450
  // don't need to actually scale it up/down.
2465
3451
  //
2466
3452
  // If parentSideEffectStep exists then parentLayerPlan must exist.
2467
- ensurePlanAvailableInLayer($sideEffect, layerPlan.parentLayerPlan);
3453
+ if (layerPlan.reason.type === "root") {
3454
+ // No action
3455
+ }
3456
+ else if (layerPlan.reason.type === "combined") {
3457
+ throw new Error(`GrafastInternalError<26f6b639-637b-4db5-980b-294697317e35>: combined layer plan ${layerPlan} is not permitted to reference side effects`);
3458
+ }
3459
+ else {
3460
+ ensurePlanAvailableInLayer($sideEffect, layerPlan.reason.parentLayerPlan);
3461
+ }
2468
3462
  }
2469
3463
  // Copy polymorphic parentStepId
2470
3464
  if (layerPlan.reason.type === "polymorphic") {
@@ -2474,17 +3468,31 @@ class OperationPlan {
2474
3468
  // Ensure list is accessible in parent layerPlan
2475
3469
  if (layerPlan.reason.type === "listItem") {
2476
3470
  const parentStep = layerPlan.reason.parentStep;
2477
- ensurePlanAvailableInLayer(parentStep, layerPlan.parentLayerPlan);
3471
+ ensurePlanAvailableInLayer(parentStep, layerPlan.reason.parentLayerPlan);
2478
3472
  const stream = layerPlan.reason.stream;
2479
3473
  if (stream != null) {
2480
3474
  if (stream.initialCountStepId) {
2481
- ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.initialCountStepId), layerPlan.parentLayerPlan);
3475
+ ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.initialCountStepId), layerPlan.reason.parentLayerPlan);
2482
3476
  }
2483
3477
  if (stream.ifStepId) {
2484
- ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.ifStepId), layerPlan.parentLayerPlan);
3478
+ ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.ifStepId), layerPlan.reason.parentLayerPlan);
2485
3479
  }
2486
3480
  if (stream.labelStepId) {
2487
- ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.labelStepId), layerPlan.parentLayerPlan);
3481
+ ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.labelStepId), layerPlan.reason.parentLayerPlan);
3482
+ }
3483
+ }
3484
+ }
3485
+ if (layerPlan.reason.type === "combined") {
3486
+ // Loop through combinations and ensure source step is available in parent layer plan
3487
+ for (const combo of layerPlan.combinations) {
3488
+ for (const source of combo.sources) {
3489
+ const { stepId, layerPlanId } = source;
3490
+ const sourceLayerPlan = layerPlan.reason.parentLayerPlans.find((p) => p.id === layerPlanId);
3491
+ if (!sourceLayerPlan) {
3492
+ throw new Error(`GrafastInternalError<86d15f1e-7ec8-4095-869a-548daa2cea0e>: Could not find the parent layer plan ${layerPlanId} of ${layerPlan}`);
3493
+ }
3494
+ const step = this.stepTracker.getStepById(stepId);
3495
+ ensurePlanAvailableInLayer(step, sourceLayerPlan);
2488
3496
  }
2489
3497
  }
2490
3498
  }
@@ -2540,6 +3548,7 @@ class OperationPlan {
2540
3548
  dependencyIds: sstep.dependencies.map((d) => d.id),
2541
3549
  dependencyForbiddenFlags: sstep.dependencyForbiddenFlags.slice(),
2542
3550
  dependencyOnReject: sstep.dependencyOnReject.map((or) => or ? String(or) : or),
3551
+ dependencyDataOnly: sstep.dependencyDataOnly.slice(),
2543
3552
  polymorphicPaths: step.polymorphicPaths
2544
3553
  ? [...step.polymorphicPaths]
2545
3554
  : undefined,
@@ -2558,7 +3567,8 @@ class OperationPlan {
2558
3567
  unbatchedStepIds: phase.unbatchedSyncAndSafeSteps?.map((s) => s.step.id),
2559
3568
  };
2560
3569
  }
2561
- function printBucketReason(reason) {
3570
+ function printBucketReason(layerPlan) {
3571
+ const { reason } = layerPlan;
2562
3572
  switch (reason.type) {
2563
3573
  case "root": {
2564
3574
  const { type } = reason;
@@ -2588,15 +3598,37 @@ class OperationPlan {
2588
3598
  const { type, typeNames, parentStep, polymorphicPaths } = reason;
2589
3599
  return {
2590
3600
  type,
2591
- typeNames,
3601
+ typeNames: [...typeNames],
2592
3602
  parentStepId: parentStep.id,
2593
3603
  polymorphicPaths: [...polymorphicPaths],
2594
3604
  };
2595
3605
  }
3606
+ case "polymorphicPartition": {
3607
+ const { type, typeNames, polymorphicPaths } = reason;
3608
+ return {
3609
+ type,
3610
+ typeNames: [...typeNames],
3611
+ polymorphicPaths: [...polymorphicPaths],
3612
+ };
3613
+ }
2596
3614
  case "subroutine": {
2597
3615
  const { type, parentStep } = reason;
2598
3616
  return { type, parentStepId: parentStep.id };
2599
3617
  }
3618
+ case "combined": {
3619
+ const { type, parentLayerPlans } = reason;
3620
+ return {
3621
+ type,
3622
+ parentLayerPlanIds: [...parentLayerPlans].map((l) => l.id),
3623
+ combinations: layerPlan.combinations.map((c) => ({
3624
+ sources: c.sources.map((f) => ({
3625
+ layerPlanId: f.layerPlanId,
3626
+ stepId: f.stepId,
3627
+ })),
3628
+ targetStepId: c.targetStepId,
3629
+ })),
3630
+ };
3631
+ }
2600
3632
  default: {
2601
3633
  const never = reason;
2602
3634
  throw new Error(`Failed to process layer plan reason ${(0, inspect_js_1.inspect)(never)}`);
@@ -2604,21 +3636,22 @@ class OperationPlan {
2604
3636
  }
2605
3637
  }
2606
3638
  function printBucket(lp) {
2607
- lp.reason;
2608
3639
  return {
2609
3640
  id: lp.id,
2610
- reason: printBucketReason(lp.reason),
3641
+ reason: printBucketReason(lp),
2611
3642
  parentSideEffectStepId: lp.parentSideEffectStep?.id ?? null,
2612
3643
  copyStepIds: lp.copyStepIds,
2613
3644
  phases: lp.phases.map(printPhase),
2614
3645
  steps: lp.steps.map(printStep),
2615
- children: lp.children.map(printBucket),
3646
+ childIds: lp.children.map((c) => c.id),
2616
3647
  rootStepId: lp.rootStep ? lp.rootStep.id : null,
2617
3648
  };
2618
3649
  }
2619
3650
  return {
2620
3651
  version: "v1",
2621
- rootBucket: printBucket(this.rootLayerPlan),
3652
+ buckets: this.stepTracker.layerPlans
3653
+ .filter((b) => b != null)
3654
+ .map(printBucket),
2622
3655
  };
2623
3656
  }
2624
3657
  finishSubroutine(subroutineStep, layerPlan) {
@@ -2672,16 +3705,25 @@ class OperationPlan {
2672
3705
  * need to regenerate it on future calls, significantly reducing the load on
2673
3706
  * deduplication later.
2674
3707
  *
2675
- * Note: automatically extends the cached step into other (relevant)
2676
- * polymorphic paths; if this shouldn't be the case then don't use cacheStep
2677
- * and instead rely on deduplication as usual.
2678
- *
2679
3708
  * @experimental
2680
3709
  */
2681
3710
  cacheStep(ownerStep, actionKey, cacheKey, cb) {
2682
3711
  const layerPlan = (0, withGlobalLayerPlan_js_1.currentLayerPlan)();
3712
+ let cache = this._cacheStepStoreByActionKeyByLayerPlan.get(layerPlan.id);
3713
+ if (!cache) {
3714
+ cache = Object.create(null);
3715
+ this._cacheStepStoreByActionKeyByLayerPlan.set(layerPlan.id, cache);
3716
+ }
3717
+ return this._cacheStep(cache, ownerStep, actionKey, cacheKey, cb);
3718
+ }
3719
+ /** @internal */
3720
+ cacheImmutableStep(actionKey, cacheKey /* Anything suitable to be the key in a Map */, cb) {
3721
+ const cache = this._immutableCacheStepStoreAndActionKey;
3722
+ return this._cacheStep(cache, this.variableValuesStep, actionKey, cacheKey, cb);
3723
+ }
3724
+ _cacheStep(store, ownerStep, actionKey, cacheKey /* Anything suitable to be the key in a Map */, cb) {
2683
3725
  const paths = (0, withGlobalLayerPlan_js_1.currentPolymorphicPaths)();
2684
- const cache = (this._cacheStepStoreByLayerPlanAndActionKey[`${actionKey}|${layerPlan.id}|${ownerStep.id}`] ??= new Map());
3726
+ const cache = (store[`${actionKey}|${ownerStep.id}|${paths == null ? "" : [...paths].join(",")}`] ??= new Map());
2685
3727
  const cacheIt = () => {
2686
3728
  const stepToCache = cb();
2687
3729
  if (!(stepToCache instanceof index_js_1.Step)) {
@@ -2696,14 +3738,6 @@ class OperationPlan {
2696
3738
  const cachedStepId = cache.get(cacheKey);
2697
3739
  const cachedStep = this.stepTracker.stepById[cachedStepId];
2698
3740
  if (cachedStep) {
2699
- // Fix poly paths
2700
- if (paths && cachedStep.polymorphicPaths) {
2701
- const polymorphicPaths = new Set([
2702
- ...cachedStep.polymorphicPaths,
2703
- ...paths,
2704
- ]);
2705
- cachedStep.polymorphicPaths = polymorphicPaths;
2706
- }
2707
3741
  return cachedStep;
2708
3742
  }
2709
3743
  else {
@@ -2715,10 +3749,10 @@ class OperationPlan {
2715
3749
  * from setting hasSideEffects on an ExecutableStep, among other places.
2716
3750
  */
2717
3751
  resetCache() {
2718
- this._cacheStepStoreByLayerPlanAndActionKey = Object.create(null);
3752
+ this._cacheStepStoreByActionKeyByLayerPlan.clear();
2719
3753
  }
2720
3754
  withRootLayerPlan(cb) {
2721
- return (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, cb);
3755
+ return (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, null, cb);
2722
3756
  }
2723
3757
  }
2724
3758
  exports.OperationPlan = OperationPlan;
@@ -2832,4 +3866,96 @@ function throwNoNewStepsError(operationPlan, actionDescription, step, previousSt
2832
3866
  .map((s) => String(s))
2833
3867
  .join(", ")}`);
2834
3868
  }
3869
+ function isSafeForUnbatched(step) {
3870
+ // Never run (potential) stream steps as unbatched, otherwise they won't be
3871
+ // able to stream!
3872
+ if (step._stepOptions.stream)
3873
+ return false;
3874
+ // Non-unary steps are safe for unbatched execution
3875
+ if (step._isUnary === false)
3876
+ return true;
3877
+ // Unary steps are only safe for unbatched execution _if_ their polymorphic
3878
+ // paths match that of their bucket.
3879
+ // !! search "f4ce2c83"
3880
+ if (step.polymorphicPaths === null)
3881
+ return true;
3882
+ // PERF: this can probably be optimized further
3883
+ return false;
3884
+ }
3885
+ function defaultPlanType($stepOrSpecifier, info) {
3886
+ const $__typename = $stepOrSpecifier.toTypename?.() ??
3887
+ (info.resolverEmulation
3888
+ ? (0, graphqlResolver_js_1.graphqlResolveType)($stepOrSpecifier, info)
3889
+ : (0, index_js_1.get)($stepOrSpecifier, "__typename"));
3890
+ return { $__typename };
3891
+ }
3892
+ function polymorphicPathsForLayer(layer) {
3893
+ switch (layer.reason.type) {
3894
+ case "polymorphic":
3895
+ case "polymorphicPartition": {
3896
+ return layer.reason.polymorphicPaths;
3897
+ }
3898
+ case "combined": {
3899
+ const set = new Set();
3900
+ for (const lp of layer.reason.parentLayerPlans) {
3901
+ for (const p of polymorphicPathsForLayer(lp)) {
3902
+ set.add(p);
3903
+ }
3904
+ }
3905
+ return set;
3906
+ }
3907
+ case "root": {
3908
+ return null;
3909
+ }
3910
+ default: {
3911
+ return polymorphicPathsForLayer(layer.reason.parentLayerPlan);
3912
+ }
3913
+ }
3914
+ }
3915
+ function stepIsValidInPolyPath($step, polyPath) {
3916
+ if (!$step.polymorphicPaths)
3917
+ return true;
3918
+ let trimmed = polyPath;
3919
+ do {
3920
+ if ($step.polymorphicPaths.has(trimmed))
3921
+ return true;
3922
+ const i = trimmed.lastIndexOf(">");
3923
+ trimmed = trimmed.slice(0, i);
3924
+ } while (trimmed.length > 0 && trimmed[0] === ">");
3925
+ return false;
3926
+ }
3927
+ function isPeerLayerPlan(lp1, lp2) {
3928
+ if (lp1 == null || lp2 == null) {
3929
+ return false;
3930
+ }
3931
+ if (lp1 === lp2)
3932
+ return true;
3933
+ if (lp1.reason.type === "polymorphicPartition" &&
3934
+ lp2.reason.type === "polymorphicPartition" &&
3935
+ lp1.reason.parentLayerPlan === lp2.reason.parentLayerPlan) {
3936
+ return true;
3937
+ }
3938
+ return false;
3939
+ }
3940
+ /**
3941
+ * NOTE: includes `start` itself, so it's not _truly_ descendents
3942
+ */
3943
+ function getDescendents(start, childrenByLayerPlan) {
3944
+ const descendents = new Set();
3945
+ const queue = [start];
3946
+ while (queue.length > 0) {
3947
+ const current = queue.pop();
3948
+ // Already seen
3949
+ if (descendents.has(current))
3950
+ continue;
3951
+ descendents.add(current);
3952
+ const children = childrenByLayerPlan.get(current);
3953
+ if (children) {
3954
+ for (const child of children) {
3955
+ queue.push(child);
3956
+ }
3957
+ }
3958
+ }
3959
+ return descendents;
3960
+ }
2835
3961
  //# sourceMappingURL=OperationPlan.js.map