grafast 0.1.1-beta.20 → 0.1.1-beta.21

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 (133) hide show
  1. package/dist/args.js +2 -3
  2. package/dist/assert.js +2 -3
  3. package/dist/constraints.js +1 -2
  4. package/dist/deferred.js +1 -2
  5. package/dist/dev.js +2 -2
  6. package/dist/engine/LayerPlan.d.ts +20 -16
  7. package/dist/engine/LayerPlan.js +5 -10
  8. package/dist/engine/OperationPlan.d.ts +12 -13
  9. package/dist/engine/OperationPlan.js +210 -146
  10. package/dist/engine/OutputPlan.d.ts +4 -4
  11. package/dist/engine/OutputPlan.js +13 -10
  12. package/dist/engine/StepTracker.js +11 -2
  13. package/dist/engine/executeBucket.js +135 -94
  14. package/dist/engine/executeOutputPlan.js +1 -2
  15. package/dist/engine/lib/defaultPlanResolver.d.ts +6 -0
  16. package/dist/engine/lib/defaultPlanResolver.js +11 -0
  17. package/dist/engine/lib/withGlobalLayerPlan.d.ts +2 -2
  18. package/dist/engine/lib/withGlobalLayerPlan.js +4 -5
  19. package/dist/engine/lock.d.ts +3 -3
  20. package/dist/engine/lock.js +3 -3
  21. package/dist/error.js +4 -4
  22. package/dist/establishOperationPlan.js +2 -3
  23. package/dist/execute.js +3 -4
  24. package/dist/exportAs.js +2 -3
  25. package/dist/global.js +2 -3
  26. package/dist/grafastGraphql.js +2 -3
  27. package/dist/grafastPrint.js +7 -8
  28. package/dist/graphqlCollectFields.js +6 -7
  29. package/dist/graphqlMergeSelectionSets.js +1 -2
  30. package/dist/index.d.ts +22 -18
  31. package/dist/index.js +41 -16
  32. package/dist/input.js +75 -28
  33. package/dist/inspect.d.ts +9 -4
  34. package/dist/inspect.js +2 -2
  35. package/dist/interfaces.d.ts +110 -89
  36. package/dist/makeGrafastSchema.d.ts +18 -9
  37. package/dist/makeGrafastSchema.js +64 -30
  38. package/dist/mermaid.js +53 -14
  39. package/dist/middleware.js +1 -2
  40. package/dist/multistep.d.ts +6 -6
  41. package/dist/multistep.js +6 -7
  42. package/dist/operationPlan-input.d.ts +5 -4
  43. package/dist/operationPlan-input.js +131 -399
  44. package/dist/planJSONInterfaces.d.ts +6 -2
  45. package/dist/polymorphic.js +4 -5
  46. package/dist/prepare.js +6 -6
  47. package/dist/step.d.ts +67 -99
  48. package/dist/step.js +99 -127
  49. package/dist/steps/__flag.d.ts +16 -16
  50. package/dist/steps/__flag.js +9 -6
  51. package/dist/steps/__inputDefault.d.ts +14 -0
  52. package/dist/steps/__inputDefault.js +48 -0
  53. package/dist/steps/__inputDynamicScalar.d.ts +2 -4
  54. package/dist/steps/__inputDynamicScalar.js +4 -2
  55. package/dist/steps/__inputList.d.ts +5 -9
  56. package/dist/steps/__inputList.js +4 -1
  57. package/dist/steps/__inputObject.d.ts +6 -11
  58. package/dist/steps/__inputObject.js +13 -3
  59. package/dist/steps/__inputStaticLeaf.d.ts +2 -4
  60. package/dist/steps/__inputStaticLeaf.js +3 -1
  61. package/dist/steps/__item.d.ts +6 -6
  62. package/dist/steps/__item.js +1 -1
  63. package/dist/steps/__trackedValue.d.ts +4 -49
  64. package/dist/steps/__trackedValue.js +24 -6
  65. package/dist/steps/__value.d.ts +3 -3
  66. package/dist/steps/__value.js +3 -2
  67. package/dist/steps/access.d.ts +7 -6
  68. package/dist/steps/access.js +14 -7
  69. package/dist/steps/applyInput.d.ts +44 -0
  70. package/dist/steps/applyInput.js +206 -0
  71. package/dist/steps/applyTransforms.d.ts +2 -2
  72. package/dist/steps/applyTransforms.js +7 -5
  73. package/dist/steps/bakedInput.d.ts +23 -0
  74. package/dist/steps/bakedInput.js +90 -0
  75. package/dist/steps/condition.d.ts +5 -5
  76. package/dist/steps/condition.js +3 -3
  77. package/dist/steps/connection.d.ts +42 -39
  78. package/dist/steps/connection.js +51 -35
  79. package/dist/steps/constant.d.ts +4 -8
  80. package/dist/steps/constant.js +54 -6
  81. package/dist/steps/each.d.ts +3 -3
  82. package/dist/steps/each.js +1 -2
  83. package/dist/steps/error.d.ts +2 -2
  84. package/dist/steps/error.js +3 -3
  85. package/dist/steps/filter.d.ts +3 -2
  86. package/dist/steps/filter.js +1 -2
  87. package/dist/steps/first.d.ts +6 -5
  88. package/dist/steps/first.js +5 -4
  89. package/dist/steps/graphqlResolver.d.ts +2 -2
  90. package/dist/steps/graphqlResolver.js +44 -56
  91. package/dist/steps/groupBy.d.ts +3 -2
  92. package/dist/steps/groupBy.js +1 -2
  93. package/dist/steps/index.d.ts +4 -2
  94. package/dist/steps/index.js +21 -9
  95. package/dist/steps/lambda.d.ts +5 -5
  96. package/dist/steps/lambda.js +4 -4
  97. package/dist/steps/last.d.ts +6 -5
  98. package/dist/steps/last.js +5 -4
  99. package/dist/steps/list.d.ts +7 -7
  100. package/dist/steps/list.js +3 -3
  101. package/dist/steps/listTransform.d.ts +14 -12
  102. package/dist/steps/listTransform.js +14 -7
  103. package/dist/steps/listen.d.ts +6 -8
  104. package/dist/steps/listen.js +7 -7
  105. package/dist/steps/load.d.ts +6 -6
  106. package/dist/steps/load.js +7 -7
  107. package/dist/steps/node.d.ts +16 -10
  108. package/dist/steps/node.js +14 -7
  109. package/dist/steps/object.d.ts +11 -11
  110. package/dist/steps/object.js +5 -5
  111. package/dist/steps/partitionByIndex.d.ts +3 -2
  112. package/dist/steps/partitionByIndex.js +1 -2
  113. package/dist/steps/polymorphicBranch.d.ts +8 -8
  114. package/dist/steps/polymorphicBranch.js +4 -4
  115. package/dist/steps/proxy.d.ts +8 -8
  116. package/dist/steps/proxy.js +3 -3
  117. package/dist/steps/remapKeys.d.ts +6 -6
  118. package/dist/steps/remapKeys.js +3 -3
  119. package/dist/steps/reverse.d.ts +5 -5
  120. package/dist/steps/reverse.js +4 -4
  121. package/dist/steps/setter.d.ts +9 -18
  122. package/dist/steps/setter.js +24 -14
  123. package/dist/steps/sideEffect.d.ts +4 -4
  124. package/dist/steps/sideEffect.js +3 -3
  125. package/dist/stringifyPayload.js +1 -2
  126. package/dist/stripAnsi.js +2 -2
  127. package/dist/subscribe.js +2 -3
  128. package/dist/timeSource.d.ts +0 -1
  129. package/dist/utils.d.ts +43 -31
  130. package/dist/utils.js +97 -61
  131. package/dist/version.d.ts +1 -1
  132. package/dist/version.js +1 -1
  133. package/package.json +2 -2
@@ -14,12 +14,13 @@ const inspect_js_1 = require("../inspect.js");
14
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
- const access_js_1 = require("../steps/access.js");
17
+ const connection_js_1 = require("../steps/connection.js");
18
18
  const constant_js_1 = require("../steps/constant.js");
19
19
  const graphqlResolver_js_1 = require("../steps/graphqlResolver.js");
20
20
  const timeSource_js_1 = require("../timeSource.js");
21
21
  const utils_js_1 = require("../utils.js");
22
22
  const LayerPlan_js_1 = require("./LayerPlan.js");
23
+ const defaultPlanResolver_js_1 = require("./lib/defaultPlanResolver.js");
23
24
  const withGlobalLayerPlan_js_1 = require("./lib/withGlobalLayerPlan.js");
24
25
  const lock_js_1 = require("./lock.js");
25
26
  const OutputPlan_js_1 = require("./OutputPlan.js");
@@ -55,7 +56,7 @@ const MAX_OPTIMIZATION_LOOPS = 10;
55
56
  const REASON_ROOT = Object.freeze({ type: "root" });
56
57
  const OUTPUT_PLAN_TYPE_NULL = Object.freeze({ mode: "null" });
57
58
  const OUTPUT_PLAN_TYPE_ARRAY = Object.freeze({ mode: "array" });
58
- const newValueStepCallback = () => new index_js_1.__ValueStep();
59
+ const newValueStepCallback = (isImmutable) => new index_js_1.__ValueStep(isImmutable);
59
60
  const NO_ARGS = {
60
61
  get() {
61
62
  throw new Error(`This field doesn't have any arguments`);
@@ -89,13 +90,8 @@ class OperationPlan {
89
90
  this.loc = index_js_1.isDev
90
91
  ? []
91
92
  : null /* forbid loc in production */;
92
- this.modifierStepCount = 0;
93
- this.modifierDepthCount = 0;
94
- this.modifierSteps = [];
95
93
  /** @internal */
96
94
  this.stepTracker = new StepTracker_js_1.StepTracker(this);
97
- this.maxDeduplicatedStepId = -1;
98
- this.maxValidatedStepId = -1;
99
95
  /**
100
96
  * @internal
101
97
  */
@@ -108,6 +104,8 @@ class OperationPlan {
108
104
  this.previousLap = this.startTime;
109
105
  this.laps = [];
110
106
  this.optimizeMeta = new Map();
107
+ /** @internal */
108
+ this.valueNodeToStaticValueCache = new Map();
111
109
  /** @internal Use plan.getStep(id) instead. */
112
110
  this.getStep = index_js_1.isDev
113
111
  ? (id, requestingStep) => {
@@ -320,21 +318,6 @@ class OperationPlan {
320
318
  }
321
319
  return this.stepTracker.addStep(plan);
322
320
  }
323
- /**
324
- * Adds a plan to the known steps and returns the number to use as the plan
325
- * id. ONLY to be used from Step, user code should never call this directly.
326
- *
327
- * @internal
328
- */
329
- _addModifierStep(step) {
330
- if (!["plan", "validate", "optimize"].includes(this.phase)) {
331
- throw new Error(`Creating a step during the '${this.phase}' phase is forbidden.`);
332
- }
333
- const modifierStepId = `${this.modifierDepthCount}-${this
334
- .modifierStepCount++}`;
335
- this.modifierSteps.push(step);
336
- return modifierStepId;
337
- }
338
321
  /**
339
322
  * Get a plan without specifying who requested it; this disables all the
340
323
  * caller checks. Only intended to be called from internal code.
@@ -473,14 +456,10 @@ class OperationPlan {
473
456
  : NO_ARGS;
474
457
  if (subscriptionPlanResolver !== undefined) {
475
458
  // PERF: optimize this
476
- const { haltTree, step: subscribeStep } = this.planField(this.rootLayerPlan, path, exports.POLYMORPHIC_ROOT_PATHS, fields, subscriptionPlanResolver, "autoApplyAfterParentSubscribePlan", this.trackedRootValueStep, fieldSpec, trackedArguments);
459
+ const { haltTree, step: subscribeStep } = this.planField(rootType.name, fieldName, this.rootLayerPlan, path, exports.POLYMORPHIC_ROOT_PATHS, subscriptionPlanResolver, "subscribePlan", this.trackedRootValueStep, fieldSpec, trackedArguments, true);
477
460
  if (haltTree) {
478
461
  throw new index_js_1.SafeError("Failed to setup subscription");
479
462
  }
480
- const stepOptions = {
481
- stream: { initialCount: 0 },
482
- };
483
- subscribeStep._stepOptions = stepOptions;
484
463
  this.rootLayerPlan.setRootStep(subscribeStep);
485
464
  const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, this.rootLayerPlan, {
486
465
  type: "subscription",
@@ -521,10 +500,8 @@ class OperationPlan {
521
500
  },
522
501
  });
523
502
  });
524
- const stepOptions = {
525
- stream: { initialCount: 0 },
526
- };
527
- subscribeStep._stepOptions = stepOptions;
503
+ subscribeStep._stepOptions.stream = {};
504
+ subscribeStep._stepOptions.walkIterable = true;
528
505
  this.rootLayerPlan.setRootStep(subscribeStep);
529
506
  const subscriptionEventLayerPlan = new LayerPlan_js_1.LayerPlan(this, this.rootLayerPlan, {
530
507
  type: "subscription",
@@ -548,7 +525,7 @@ class OperationPlan {
548
525
  * Gets the item plan for a given parent list plan - this ensures we only
549
526
  * create one item plan per parent plan.
550
527
  */
551
- itemStepForListStep(parentLayerPlan, listStep, depth = 0) {
528
+ itemStepForListStep(parentLayerPlan, listStep, depth, stream) {
552
529
  const itemStepId = this.itemStepIdByListStepId[listStep.id];
553
530
  if (itemStepId !== undefined) {
554
531
  return this.stepTracker.getStepById(itemStepId);
@@ -557,7 +534,7 @@ class OperationPlan {
557
534
  const layerPlan = new LayerPlan_js_1.LayerPlan(this, parentLayerPlan, {
558
535
  type: "listItem",
559
536
  parentStep: listStep,
560
- stream: listStep._stepOptions.stream ?? undefined,
537
+ stream,
561
538
  });
562
539
  const itemPlan = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, listStep.polymorphicPaths, () => new index_js_1.__ItemStep(listStep, depth));
563
540
  layerPlan.setRootStep(itemPlan);
@@ -633,8 +610,7 @@ class OperationPlan {
633
610
  ? defaultFieldResolver
634
611
  : null;
635
612
  // Apply a default plan to fields that do not have a plan nor a resolver.
636
- const planResolver = rawPlanResolver ??
637
- (resolver ? undefined : makeDefaultPlan(fieldName));
613
+ const planResolver = rawPlanResolver ?? (resolver ? undefined : defaultPlanResolver_js_1.defaultPlanResolver);
638
614
  /*
639
615
  * When considering resolvers on fields, there's three booleans to
640
616
  * consider:
@@ -717,8 +693,49 @@ class OperationPlan {
717
693
  ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, this.getTrackedArguments, this, objectFieldArgs, field)
718
694
  : NO_ARGS;
719
695
  const fieldPath = [...path, responseKey];
696
+ let streamDetails = null;
697
+ const isList = isListType(getNullableType(fieldType));
698
+ if (isList) {
699
+ // read the @stream directive, if present
700
+ // TODO: Check SameStreamDirective still exists in @stream spec at release.
701
+ /*
702
+ * `SameStreamDirective`
703
+ * (https://github.com/graphql/graphql-spec/blob/26fd78c4a89a79552dcc0c7e0140a975ce654400/spec/Section%205%20--%20Validation.md#L450-L458)
704
+ * ensures that every field that has `@stream` must have the same
705
+ * `@stream` arguments; so we can just check the first node in the
706
+ * merged set to see our stream options. NOTE: if this changes before
707
+ * release then we may need to find the stream with the largest
708
+ * `initialCount` to figure what to do; something like:
709
+ *
710
+ * const streamDirective = firstField.directives?.filter(
711
+ * (d) => d.name.value === "stream",
712
+ * ).sort(
713
+ * (a, z) => getArg(z, 'initialCount', 0) - getArg(a, 'initialCount', 0)
714
+ * )[0]
715
+ */
716
+ for (const n of fieldNodes) {
717
+ const streamDirective = n.directives?.find((d) => d.name.value === "stream");
718
+ if (streamDirective === undefined) {
719
+ // Undo any previous stream details; the non-@stream wins.
720
+ streamDetails = null;
721
+ break;
722
+ }
723
+ else if (streamDetails !== null) {
724
+ // Validation promises the values are the same
725
+ continue;
726
+ }
727
+ else {
728
+ // Create streamDetails
729
+ streamDetails = this.withRootLayerPlan(() => ({
730
+ initialCount: this.internalDependency((0, utils_js_1.directiveArgument)(this, streamDirective, "initialCount", graphql.Kind.INT) ?? (0, constant_js_1.constant)(0)),
731
+ if: this.internalDependency((0, utils_js_1.directiveArgument)(this, streamDirective, "if", graphql.Kind.BOOLEAN) ?? (0, constant_js_1.constant)(true)),
732
+ label: this.internalDependency((0, utils_js_1.directiveArgument)(this, streamDirective, "label", graphql.Kind.STRING) ?? (0, constant_js_1.constant)(undefined)),
733
+ }));
734
+ }
735
+ }
736
+ }
720
737
  if (typeof planResolver === "function") {
721
- ({ step, haltTree } = this.planField(fieldLayerPlan, fieldPath, polymorphicPaths, fieldNodes, planResolver, "autoApplyAfterParentPlan", parentStep, objectField, trackedArguments));
738
+ ({ step, haltTree } = this.planField(objectType.name, fieldName, fieldLayerPlan, fieldPath, polymorphicPaths, planResolver, "plan", parentStep, objectField, trackedArguments, isList ? (streamDetails ?? false) : null));
722
739
  }
723
740
  else {
724
741
  // No plan resolver (or plan resolver fallback) so there must be a
@@ -764,7 +781,7 @@ class OperationPlan {
764
781
  // If one field has a selection set, they all have a selection set (guaranteed by validation).
765
782
  field.selectionSet != null
766
783
  ? fieldNodes.flatMap((n) => n.selectionSet.selections)
767
- : undefined, objectType, responseKey, fieldType, step, locationDetails, resolverEmulation);
784
+ : undefined, objectType, responseKey, fieldType, step, locationDetails, resolverEmulation, 0, streamDetails);
768
785
  }
769
786
  }
770
787
  finally {
@@ -818,29 +835,54 @@ class OperationPlan {
818
835
  if (this.loc !== null)
819
836
  this.loc.pop();
820
837
  }
838
+ internalDependency($step) {
839
+ this.stepTracker.internalDependencies.add($step);
840
+ return $step;
841
+ }
821
842
  // Similar to the old 'planFieldReturnType'
822
843
  planIntoOutputPlan(parentOutputPlan,
823
844
  // Typically this is parentOutputPlan.layerPlan; but in the case of mutationFields it isn't.
824
845
  parentLayerPlan,
825
846
  // This is the LAYER-RELATIVE path, not the absolute path! It resets!
826
- path, polymorphicPath, polymorphicPaths, selections, parentObjectType, responseKey, fieldType, $step, locationDetails, resolverEmulation, listDepth = 0) {
847
+ path, polymorphicPath, polymorphicPaths, selections, parentObjectType, responseKey, fieldType, $step, locationDetails, resolverEmulation, listDepth, streamDetails) {
827
848
  const nullableFieldType = getNullableType(fieldType);
828
849
  const isNonNull = nullableFieldType !== fieldType;
829
850
  if (isListType(nullableFieldType)) {
830
- const listOutputPlan = new OutputPlan_js_1.OutputPlan(parentLayerPlan, $step, OUTPUT_PLAN_TYPE_ARRAY, locationDetails);
851
+ const $list = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(parentLayerPlan, polymorphicPaths, connection_js_1.itemsOrStep, null, $step);
852
+ if ($list !== $step) {
853
+ $list._stepOptions.stream = $step._stepOptions.stream;
854
+ }
855
+ $list._stepOptions.walkIterable = true;
856
+ const listOutputPlan = new OutputPlan_js_1.OutputPlan(parentLayerPlan, $list, OUTPUT_PLAN_TYPE_ARRAY, locationDetails);
831
857
  parentOutputPlan.addChild(parentObjectType, responseKey, {
832
858
  type: "outputPlan",
833
859
  outputPlan: listOutputPlan,
834
860
  isNonNull,
835
861
  locationDetails,
836
862
  });
837
- const $__item = this.itemStepForListStep(parentLayerPlan, $step, listDepth);
863
+ const stream = streamDetails
864
+ ? {
865
+ // These are already marked as internal dependencies
866
+ initialCountStepId: streamDetails.initialCount.id,
867
+ ifStepId: streamDetails.if.id,
868
+ labelStepId: streamDetails.label.id,
869
+ }
870
+ : undefined;
871
+ let $__item = this.itemStepForListStep(parentLayerPlan, $list, listDepth, stream);
838
872
  const $sideEffect = $__item.layerPlan.latestSideEffectStep;
839
873
  try {
840
- const $item = (0, step_js_1.isListCapableStep)($step)
841
- ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)($__item.layerPlan, $__item.polymorphicPaths, $step.listItem, $step, $__item)
842
- : $__item;
843
- this.planIntoOutputPlan(listOutputPlan, $item.layerPlan, path, polymorphicPath, polymorphicPaths, selections, null, null, nullableFieldType.ofType, $item, locationDetails, resolverEmulation, listDepth + 1);
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);
844
886
  }
845
887
  finally {
846
888
  $__item.layerPlan.latestSideEffectStep = $sideEffect;
@@ -894,8 +936,8 @@ class OperationPlan {
894
936
  const stepAssertion = nullableFieldType.extensions?.grafast?.assertStep;
895
937
  if (stepAssertion != null) {
896
938
  try {
897
- if (stepAssertion === index_js_1.ExecutableStep ||
898
- stepAssertion.prototype instanceof index_js_1.ExecutableStep) {
939
+ if (stepAssertion === index_js_1.Step ||
940
+ stepAssertion.prototype instanceof index_js_1.Step) {
899
941
  if (!($step instanceof stepAssertion)) {
900
942
  throw new Error(`Step mis-match: expected ${stepAssertion.name}, but instead found ${$step.constructor.name} (${$step})`);
901
943
  }
@@ -1112,7 +1154,13 @@ class OperationPlan {
1112
1154
  return layerPlan;
1113
1155
  }
1114
1156
  }
1115
- planField(layerPlan, path, polymorphicPaths, fieldNodes, planResolver, applyAfterMode, rawParentStep, field, trackedArguments, deduplicate = true) {
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) {
1163
+ const coordinate = `${typeName}.${fieldName}`;
1116
1164
  // The step may have been de-duped whilst sibling steps were planned
1117
1165
  // PERF: this should be handled in the parent?
1118
1166
  const parentStep = this.stepTracker.getStepById(rawParentStep.id);
@@ -1121,7 +1169,8 @@ class OperationPlan {
1121
1169
  if (this.loc !== null)
1122
1170
  this.loc.push(`planField(${path.join(".")})`);
1123
1171
  try {
1124
- let step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, operationPlan_input_js_1.withFieldArgsForArguments, null, this, parentStep, trackedArguments, field, applyAfterMode, (fieldArgs) => planResolver(parentStep, fieldArgs, {
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, {
1173
+ fieldName,
1125
1174
  field,
1126
1175
  schema: this.schema,
1127
1176
  }));
@@ -1135,31 +1184,22 @@ class OperationPlan {
1135
1184
  haltTree = true;
1136
1185
  }
1137
1186
  (0, step_js_1.assertExecutableStep)(step);
1138
- // TODO: Check SameStreamDirective still exists in @stream spec at release.
1139
- /*
1140
- * `SameStreamDirective`
1141
- * (https://github.com/graphql/graphql-spec/blob/26fd78c4a89a79552dcc0c7e0140a975ce654400/spec/Section%205%20--%20Validation.md#L450-L458)
1142
- * ensures that every field that has `@stream` must have the same
1143
- * `@stream` arguments; so we can just check the first node in the
1144
- * merged set to see our stream options. NOTE: if this changes before
1145
- * release then we may need to find the stream with the largest
1146
- * `initialCount` to figure what to do; something like:
1147
- *
1148
- * const streamDirective = firstField.directives?.filter(
1149
- * (d) => d.name.value === "stream",
1150
- * ).sort(
1151
- * (a, z) => getArg(z, 'initialCount', 0) - getArg(a, 'initialCount', 0)
1152
- * )[0]
1153
- */
1154
- const streamDirective = fieldNodes[0].directives?.find((d) => d.name.value === "stream");
1155
- const stepOptions = {
1156
- stream: !haltTree && streamDirective
1157
- ? {
1158
- initialCount: Number((0, graphqlCollectFields_js_1.evalDirectiveArg)(fieldNodes[0], "stream", "initialCount", this.trackedVariableValuesStep, null)) || 0,
1159
- }
1160
- : null,
1161
- };
1162
- step._stepOptions = stepOptions;
1187
+ if (streamDetails === true) {
1188
+ // subscription
1189
+ step._stepOptions.stream = {};
1190
+ step._stepOptions.walkIterable = true;
1191
+ }
1192
+ else if (streamDetails === false) {
1193
+ step._stepOptions.walkIterable = true;
1194
+ }
1195
+ else if (streamDetails != null) {
1196
+ step._stepOptions.stream = {
1197
+ initialCountStepId: streamDetails.initialCount.id,
1198
+ ifStepId: streamDetails.if.id,
1199
+ labelStepId: streamDetails.label.id,
1200
+ };
1201
+ step._stepOptions.walkIterable = true;
1202
+ }
1163
1203
  if (deduplicate) {
1164
1204
  // Now that the field has been planned (including arguments, but NOT
1165
1205
  // including selection set) we can deduplicate it to see if any of its
@@ -1193,7 +1233,6 @@ class OperationPlan {
1193
1233
  }
1194
1234
  const step = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(layerPlan, polymorphicPaths, index_js_1.error, null, e);
1195
1235
  const haltTree = true;
1196
- this.modifierSteps = [];
1197
1236
  // PERF: consider deleting all steps that were allocated during this. For
1198
1237
  // now we'll just rely on tree-shaking.
1199
1238
  return { step, haltTree };
@@ -1231,42 +1270,12 @@ class OperationPlan {
1231
1270
  },
1232
1271
  };
1233
1272
  }
1234
- withModifiers(cb) {
1235
- // Stash previous modifiers
1236
- const previousModifierDepthCount = this.modifierDepthCount++;
1237
- const previousCount = this.modifierStepCount;
1238
- const previousStack = this.modifierSteps.splice(0, this.modifierSteps.length);
1239
- this.modifierStepCount = 0;
1240
- let result;
1241
- let plansToApply;
1242
- try {
1243
- result = cb();
1244
- }
1245
- finally {
1246
- // Remove the modifier plans from operationPlan and sort them ready for application.
1247
- plansToApply = this.modifierSteps
1248
- .splice(0, this.modifierSteps.length)
1249
- .reverse();
1250
- // Restore previous modifiers
1251
- this.modifierStepCount = previousCount;
1252
- for (const mod of previousStack) {
1253
- this.modifierSteps.push(mod);
1254
- }
1255
- // Restore previous depth
1256
- this.modifierDepthCount = previousModifierDepthCount;
1257
- }
1258
- // Apply the plans.
1259
- for (let i = 0, l = plansToApply.length; i < l; i++) {
1260
- plansToApply[i].apply();
1261
- }
1262
- return result;
1263
- }
1264
1273
  /**
1265
1274
  * Sets up tracking for the given value (variableValues, context, rootValue).
1266
1275
  */
1267
1276
  track(value, constraints, variableDefinitions) {
1268
- const valueStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, newValueStepCallback);
1269
- const trackedObjectStep = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, () => new index_js_1.__TrackedValueStep(value, valueStep, 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));
1270
1279
  return [valueStep, trackedObjectStep];
1271
1280
  }
1272
1281
  /**
@@ -1280,8 +1289,8 @@ class OperationPlan {
1280
1289
  continue;
1281
1290
  for (const key in step) {
1282
1291
  const val = step[key];
1283
- if (key !== ("implicitSideEffectStep") &&
1284
- val instanceof index_js_1.ExecutableStep) {
1292
+ if (key !== "implicitSideEffectStep" &&
1293
+ val instanceof index_js_1.Step) {
1285
1294
  errors.push(new Error(`ERROR: ExecutableStep ${step} has illegal reference via property '${key}' to step ${val}. You must not reference steps directly, instead add the step as a dependency (\`depId = this.addDependency($step)\`) and use \`this.getDep(depId)\` to look the step up (or, if the step is not a dependency, store the step ID and use \`this.getStep(stepId)\` each time you need to reference it). Failure to comply could result in subtle breakage during optimisation.`));
1286
1295
  }
1287
1296
  }
@@ -1421,7 +1430,24 @@ class OperationPlan {
1421
1430
  // call the function with an empty array; ignore the result.
1422
1431
  return EMPTY_ARRAY;
1423
1432
  }
1424
- const { dependencies: deps, dependencyForbiddenFlags: flags, dependencyOnReject: onReject, layerPlan: layerPlan, constructor: stepConstructor, peerKey, } = (0, utils_js_1.sudo)(step);
1433
+ // NOTE: Streams have no peers - we cannot reference the stream more than
1434
+ // once (and we aim to not cache the stream because we want its entries to
1435
+ // be garbage collected) - however if we're already fetching the list then
1436
+ // we shouldn't fetch it again via stream... We should deduplicate a stream
1437
+ // to return a non-stream.
1438
+ if (step._stepOptions.stream != null) {
1439
+ // TODO: remove this if block when we implement the new stream/defer -
1440
+ // deduplicating a stream should be fine. (Not subscriptions though - may
1441
+ // need a check for that!)
1442
+ return EMPTY_ARRAY;
1443
+ }
1444
+ if (this.stepTracker.internalDependencies.has(step)) {
1445
+ // PERF: we need to set up correct tracking, then internal deps can be deduped
1446
+ return EMPTY_ARRAY;
1447
+ }
1448
+ const sstep = (0, utils_js_1.sudo)(step);
1449
+ const { dependencies: deps, dependencyForbiddenFlags: flags, dependencyOnReject: onReject, layerPlan: layerPlan, constructor: stepConstructor, peerKey, } = sstep;
1450
+ // const streamInitialCount = sstep._stepOptions.stream?.initialCount;
1425
1451
  const dependencyCount = deps.length;
1426
1452
  if (dependencyCount === 0) {
1427
1453
  let allPeers = null;
@@ -1430,7 +1456,10 @@ class OperationPlan {
1430
1456
  if (possiblyPeer !== step &&
1431
1457
  !possiblyPeer.hasSideEffects &&
1432
1458
  possiblyPeer.layerPlan === layerPlan &&
1433
- possiblyPeer.peerKey === peerKey) {
1459
+ possiblyPeer._stepOptions.stream == null &&
1460
+ possiblyPeer.peerKey === peerKey
1461
+ // && possiblyPeer._stepOptions.stream?.initialCount === streamInitialCount
1462
+ ) {
1434
1463
  if (allPeers === null) {
1435
1464
  allPeers = [possiblyPeer];
1436
1465
  }
@@ -1457,8 +1486,11 @@ class OperationPlan {
1457
1486
  if (peerDependencyIndex !== 0 ||
1458
1487
  rawPossiblyPeer === step ||
1459
1488
  rawPossiblyPeer.hasSideEffects ||
1489
+ rawPossiblyPeer._stepOptions.stream != null ||
1460
1490
  rawPossiblyPeer.constructor !== stepConstructor ||
1461
- rawPossiblyPeer.peerKey !== peerKey) {
1491
+ rawPossiblyPeer.peerKey !== peerKey
1492
+ // || rawPossiblyPeer._stepOptions.stream?.initialCount !== streamInitialCount
1493
+ ) {
1462
1494
  continue;
1463
1495
  }
1464
1496
  const possiblyPeer = (0, utils_js_1.sudo)(rawPossiblyPeer);
@@ -1508,8 +1540,11 @@ class OperationPlan {
1508
1540
  if (peerDependencyIndex !== dependencyIndex ||
1509
1541
  rawPossiblyPeer === step ||
1510
1542
  rawPossiblyPeer.hasSideEffects ||
1543
+ rawPossiblyPeer._stepOptions.stream != null ||
1511
1544
  rawPossiblyPeer.constructor !== stepConstructor ||
1512
- rawPossiblyPeer.peerKey !== peerKey) {
1545
+ rawPossiblyPeer.peerKey !== peerKey
1546
+ // || rawPossiblyPeer._stepOptions.stream?.initialCount !== streamInitialCount
1547
+ ) {
1513
1548
  continue;
1514
1549
  }
1515
1550
  const possiblyPeer = (0, utils_js_1.sudo)(rawPossiblyPeer);
@@ -1738,6 +1773,11 @@ class OperationPlan {
1738
1773
  if (this.isImmoveable(step)) {
1739
1774
  return step;
1740
1775
  }
1776
+ if (step._isUnary) {
1777
+ step._isUnaryLocked = true;
1778
+ // Don't push unary steps down
1779
+ return step;
1780
+ }
1741
1781
  switch (step.layerPlan.reason.type) {
1742
1782
  case "root":
1743
1783
  case "subscription":
@@ -1915,7 +1955,14 @@ class OperationPlan {
1915
1955
  return { stepsAtMinDepth, equivalentSteps };
1916
1956
  }
1917
1957
  deduplicateStep(step) {
1958
+ // Conditions applied to this step are now finalized, though we may still
1959
+ // tell the step to do more stuff (like fetch extra data), it can no longer
1960
+ // change its order, conditions, etc.
1918
1961
  step.isArgumentsFinalized = true;
1962
+ // If a step is unary at this point, it must always remain unary.
1963
+ if (step._isUnary) {
1964
+ step._isUnaryLocked = true;
1965
+ }
1919
1966
  if (step.deduplicate == null)
1920
1967
  return step;
1921
1968
  const result = this._deduplicateInnerLogic(step);
@@ -1934,6 +1981,10 @@ class OperationPlan {
1934
1981
  }
1935
1982
  winner.polymorphicPaths = polymorphicPaths;
1936
1983
  }
1984
+ // Equivalent steps cannot be streaming; so we can no longer stream either.
1985
+ if (equivalentSteps.length > 0) {
1986
+ winner._stepOptions.stream = null;
1987
+ }
1937
1988
  // Give the steps a chance to pass their responsibilities to the winner.
1938
1989
  if (winner !== step) {
1939
1990
  const wasLocked = index_js_1.isDev && (0, lock_js_1.unlock)(step);
@@ -1990,7 +2041,7 @@ class OperationPlan {
1990
2041
  this.deduplicateStepsProcess(processed, start, dep);
1991
2042
  }
1992
2043
  }
1993
- (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, this.deduplicateStep, this, step);
2044
+ (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(step.layerPlan, step.polymorphicPaths, this.phase === "plan" ? this.deduplicateStep : this.hoistAndDeduplicate, this, step);
1994
2045
  }
1995
2046
  /**
1996
2047
  * Gives us a chance to replace nearly-duplicate plans with other existing
@@ -2044,27 +2095,6 @@ class OperationPlan {
2044
2095
  pushDownSteps() {
2045
2096
  this.processSteps("pushDown", "dependents-first", false, this.pushDown);
2046
2097
  }
2047
- getStepOptionsForStep(step) {
2048
- return step._stepOptions;
2049
- /*
2050
- // NOTE: streams can only be merged if their parameters are compatible
2051
- // (namely they need to have equivalent `initialCount`)
2052
- const streamLayerPlan = step.childLayerPlans.find(
2053
- (lp) => lp.reason.type === "stream",
2054
- );
2055
-
2056
- // PERF: if step isn't streamable, don't create a streamLayerPlan because
2057
- // there's no point (and then we can leverage better optimisations).
2058
-
2059
- return {
2060
- stream:
2061
- streamLayerPlan && isStreamableStep(step)
2062
- ? // && streamLayerPlan.parentStep === step
2063
- { initialCount: streamLayerPlan.reason.initialCount }
2064
- : null,
2065
- };
2066
- */
2067
- }
2068
2098
  /**
2069
2099
  * Calls the 'optimize' method on a plan, which may cause the plan to
2070
2100
  * communicate with its (deep) dependencies, and even to replace itself with
@@ -2082,7 +2112,7 @@ class OperationPlan {
2082
2112
  return step;
2083
2113
  }
2084
2114
  // We know if it's streaming or not based on the LayerPlan it's contained within.
2085
- const stepOptions = this.getStepOptionsForStep(step);
2115
+ // const stepOptions = step._stepOptions;
2086
2116
  let meta;
2087
2117
  if (step.optimizeMetaKey !== undefined) {
2088
2118
  meta = this.optimizeMeta.get(step.optimizeMetaKey);
@@ -2093,7 +2123,11 @@ class OperationPlan {
2093
2123
  }
2094
2124
  const wasLocked = index_js_1.isDev && (0, lock_js_1.unlock)(step);
2095
2125
  const replacementStep = step.optimize({
2096
- ...stepOptions,
2126
+ stream: step._stepOptions.stream
2127
+ ? {
2128
+ // We could add more details here, but for now we don't really need them?
2129
+ }
2130
+ : null,
2097
2131
  meta,
2098
2132
  });
2099
2133
  if (wasLocked)
@@ -2197,7 +2231,6 @@ class OperationPlan {
2197
2231
  /** Finalizes each step */
2198
2232
  finalizeSteps() {
2199
2233
  const initialStepCount = this.stepTracker.stepCount;
2200
- assert.strictEqual(this.modifierSteps.length, 0, "No modifier steps expected when performing finalizeSteps");
2201
2234
  for (const step of this.stepTracker.activeSteps) {
2202
2235
  const wasLocked = index_js_1.isDev && (0, lock_js_1.unlock)(step);
2203
2236
  step.finalize();
@@ -2330,7 +2363,7 @@ class OperationPlan {
2330
2363
  !(0, utils_js_1.stepADependsOnStepB)(sstep, latestSideEffectStep)) {
2331
2364
  sstep.implicitSideEffectStep = latestSideEffectStep;
2332
2365
  }
2333
- if (step.isSyncAndSafe && (0, step_js_1.isUnbatchedExecutableStep)(step)) {
2366
+ if (step.isSyncAndSafe && (0, step_js_1.isUnbatchedStep)(step)) {
2334
2367
  if (phase.unbatchedSyncAndSafeSteps !== undefined) {
2335
2368
  phase.unbatchedSyncAndSafeSteps.push({
2336
2369
  step,
@@ -2360,7 +2393,7 @@ class OperationPlan {
2360
2393
  do {
2361
2394
  foundOne = false;
2362
2395
  for (const step of pending) {
2363
- if (step.isSyncAndSafe && (0, step_js_1.isUnbatchedExecutableStep)(step)) {
2396
+ if (step.isSyncAndSafe && (0, step_js_1.isUnbatchedStep)(step)) {
2364
2397
  if (readyToExecute(step)) {
2365
2398
  processed.add(step);
2366
2399
  pending.delete(step);
@@ -2442,6 +2475,18 @@ class OperationPlan {
2442
2475
  if (layerPlan.reason.type === "listItem") {
2443
2476
  const parentStep = layerPlan.reason.parentStep;
2444
2477
  ensurePlanAvailableInLayer(parentStep, layerPlan.parentLayerPlan);
2478
+ const stream = layerPlan.reason.stream;
2479
+ if (stream != null) {
2480
+ if (stream.initialCountStepId) {
2481
+ ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.initialCountStepId), layerPlan.parentLayerPlan);
2482
+ }
2483
+ if (stream.ifStepId) {
2484
+ ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.ifStepId), layerPlan.parentLayerPlan);
2485
+ }
2486
+ if (stream.labelStepId) {
2487
+ ensurePlanAvailableInLayer(this.stepTracker.getStepById(stream.labelStepId), layerPlan.parentLayerPlan);
2488
+ }
2489
+ }
2445
2490
  }
2446
2491
  }
2447
2492
  // Populate copyPlanIds for output plans' rootStepId
@@ -2501,6 +2546,9 @@ class OperationPlan {
2501
2546
  isSyncAndSafe: step.isSyncAndSafe || undefined,
2502
2547
  supportsUnbatched: typeof step.unbatchedExecute === "function" || undefined,
2503
2548
  hasSideEffects: step.hasSideEffects || undefined,
2549
+ stream: step._stepOptions.stream
2550
+ ? { initialCountStepId: step._stepOptions.stream.initialCountStepId }
2551
+ : undefined,
2504
2552
  extra: step.planJSONExtra(),
2505
2553
  };
2506
2554
  }
@@ -2624,14 +2672,19 @@ class OperationPlan {
2624
2672
  * need to regenerate it on future calls, significantly reducing the load on
2625
2673
  * deduplication later.
2626
2674
  *
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
+ *
2627
2679
  * @experimental
2628
2680
  */
2629
2681
  cacheStep(ownerStep, actionKey, cacheKey, cb) {
2630
2682
  const layerPlan = (0, withGlobalLayerPlan_js_1.currentLayerPlan)();
2683
+ const paths = (0, withGlobalLayerPlan_js_1.currentPolymorphicPaths)();
2631
2684
  const cache = (this._cacheStepStoreByLayerPlanAndActionKey[`${actionKey}|${layerPlan.id}|${ownerStep.id}`] ??= new Map());
2632
2685
  const cacheIt = () => {
2633
2686
  const stepToCache = cb();
2634
- if (!(stepToCache instanceof index_js_1.ExecutableStep)) {
2687
+ if (!(stepToCache instanceof index_js_1.Step)) {
2635
2688
  throw new Error(`The callback passed to cacheStep must always return an ExecutableStep; but this call from ${ownerStep} returned instead ${(0, inspect_js_1.inspect)(stepToCache)}`);
2636
2689
  }
2637
2690
  cache.set(cacheKey, stepToCache.id);
@@ -2642,7 +2695,20 @@ class OperationPlan {
2642
2695
  }
2643
2696
  const cachedStepId = cache.get(cacheKey);
2644
2697
  const cachedStep = this.stepTracker.stepById[cachedStepId];
2645
- return cachedStep ?? cacheIt();
2698
+ 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
+ return cachedStep;
2708
+ }
2709
+ else {
2710
+ return cacheIt();
2711
+ }
2646
2712
  }
2647
2713
  /**
2648
2714
  * Clears the cache, typically due to side effects having taken place. Called
@@ -2651,13 +2717,11 @@ class OperationPlan {
2651
2717
  resetCache() {
2652
2718
  this._cacheStepStoreByLayerPlanAndActionKey = Object.create(null);
2653
2719
  }
2720
+ withRootLayerPlan(cb) {
2721
+ return (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.rootLayerPlan, exports.POLYMORPHIC_ROOT_PATHS, cb);
2722
+ }
2654
2723
  }
2655
2724
  exports.OperationPlan = OperationPlan;
2656
- function makeDefaultPlan(fieldName) {
2657
- return ($step) => typeof $step.get === "function"
2658
- ? $step.get(fieldName)
2659
- : (0, access_js_1.access)($step, [fieldName]);
2660
- }
2661
2725
  function makeMetaByMetaKeysFactory(allMetaKeysList) {
2662
2726
  const l = allMetaKeysList.length;
2663
2727
  // Optimize the common cases