grafast 0.1.1-beta.2 → 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 (157) hide show
  1. package/README.md +2 -3
  2. package/dist/args.d.ts +4 -7
  3. package/dist/args.js +53 -38
  4. package/dist/assert.js +2 -3
  5. package/dist/bucket.d.ts +1 -89
  6. package/dist/config.d.ts +0 -17
  7. package/dist/config.js +1 -95
  8. package/dist/constraints.d.ts +15 -1
  9. package/dist/constraints.js +41 -3
  10. package/dist/deferred.js +1 -2
  11. package/dist/dev.d.ts +1 -4
  12. package/dist/dev.js +18 -3
  13. package/dist/engine/LayerPlan.d.ts +21 -111
  14. package/dist/engine/LayerPlan.js +196 -78
  15. package/dist/engine/OperationPlan.d.ts +30 -104
  16. package/dist/engine/OperationPlan.js +763 -471
  17. package/dist/engine/OutputPlan.d.ts +9 -20
  18. package/dist/engine/OutputPlan.js +432 -444
  19. package/dist/engine/StepTracker.d.ts +1 -95
  20. package/dist/engine/StepTracker.js +94 -20
  21. package/dist/engine/executeBucket.d.ts +5 -7
  22. package/dist/engine/executeBucket.js +646 -244
  23. package/dist/engine/executeOutputPlan.d.ts +0 -53
  24. package/dist/engine/executeOutputPlan.js +7 -2
  25. package/dist/engine/lib/defaultPlanResolver.d.ts +6 -0
  26. package/dist/engine/lib/defaultPlanResolver.js +11 -0
  27. package/dist/engine/lib/withGlobalLayerPlan.d.ts +2 -0
  28. package/dist/engine/lib/withGlobalLayerPlan.js +7 -4
  29. package/dist/engine/lock.d.ts +4 -0
  30. package/dist/engine/lock.js +82 -0
  31. package/dist/envelop.js +5 -4
  32. package/dist/error.d.ts +18 -49
  33. package/dist/error.js +50 -39
  34. package/dist/establishOperationPlan.js +57 -42
  35. package/dist/execute.d.ts +5 -4
  36. package/dist/execute.js +31 -15
  37. package/dist/exportAs.d.ts +1 -18
  38. package/dist/exportAs.js +2 -3
  39. package/dist/global.js +2 -3
  40. package/dist/grafastGraphql.d.ts +6 -1
  41. package/dist/grafastGraphql.js +35 -16
  42. package/dist/grafastPrint.d.ts +3 -0
  43. package/dist/grafastPrint.js +67 -6
  44. package/dist/graphqlCollectFields.d.ts +7 -34
  45. package/dist/graphqlCollectFields.js +24 -13
  46. package/dist/graphqlMergeSelectionSets.js +1 -2
  47. package/dist/index.d.ts +44 -29
  48. package/dist/index.js +91 -18
  49. package/dist/input.d.ts +1 -9
  50. package/dist/input.js +75 -28
  51. package/dist/inspect.d.ts +9 -3
  52. package/dist/inspect.js +3 -3
  53. package/dist/interfaces.d.ts +238 -93
  54. package/dist/interfaces.js +20 -3
  55. package/dist/makeGrafastSchema.d.ts +21 -9
  56. package/dist/makeGrafastSchema.js +353 -133
  57. package/dist/mermaid.d.ts +0 -17
  58. package/dist/mermaid.js +131 -19
  59. package/dist/middleware.d.ts +7 -0
  60. package/dist/middleware.js +24 -0
  61. package/dist/multistep.d.ts +19 -0
  62. package/dist/multistep.js +54 -0
  63. package/dist/operationPlan-input.d.ts +5 -4
  64. package/dist/operationPlan-input.js +140 -368
  65. package/dist/planJSONInterfaces.d.ts +12 -2
  66. package/dist/polymorphic.js +4 -5
  67. package/dist/prepare.d.ts +1 -7
  68. package/dist/prepare.js +100 -74
  69. package/dist/step.d.ts +118 -172
  70. package/dist/step.js +262 -183
  71. package/dist/steps/__flag.d.ts +65 -0
  72. package/dist/steps/__flag.js +261 -0
  73. package/dist/steps/__inputDefault.d.ts +14 -0
  74. package/dist/steps/__inputDefault.js +48 -0
  75. package/dist/steps/__inputDynamicScalar.d.ts +4 -6
  76. package/dist/steps/__inputDynamicScalar.js +4 -2
  77. package/dist/steps/__inputList.d.ts +5 -9
  78. package/dist/steps/__inputList.js +4 -1
  79. package/dist/steps/__inputObject.d.ts +7 -11
  80. package/dist/steps/__inputObject.js +49 -3
  81. package/dist/steps/__inputStaticLeaf.d.ts +2 -4
  82. package/dist/steps/__inputStaticLeaf.js +3 -1
  83. package/dist/steps/__item.d.ts +10 -12
  84. package/dist/steps/__item.js +8 -2
  85. package/dist/steps/__trackedValue.d.ts +7 -55
  86. package/dist/steps/__trackedValue.js +69 -8
  87. package/dist/steps/__value.d.ts +5 -4
  88. package/dist/steps/__value.js +5 -4
  89. package/dist/steps/access.d.ts +10 -10
  90. package/dist/steps/access.js +50 -17
  91. package/dist/steps/applyInput.d.ts +44 -0
  92. package/dist/steps/applyInput.js +206 -0
  93. package/dist/steps/applyTransforms.d.ts +2 -25
  94. package/dist/steps/applyTransforms.js +53 -31
  95. package/dist/steps/bakedInput.d.ts +23 -0
  96. package/dist/steps/bakedInput.js +90 -0
  97. package/dist/steps/condition.d.ts +31 -0
  98. package/dist/steps/condition.js +94 -0
  99. package/dist/steps/connection.d.ts +56 -42
  100. package/dist/steps/connection.js +97 -44
  101. package/dist/steps/constant.d.ts +7 -7
  102. package/dist/steps/constant.js +86 -7
  103. package/dist/steps/each.d.ts +3 -3
  104. package/dist/steps/each.js +2 -3
  105. package/dist/steps/error.d.ts +4 -4
  106. package/dist/steps/error.js +7 -6
  107. package/dist/steps/filter.d.ts +3 -2
  108. package/dist/steps/filter.js +1 -2
  109. package/dist/steps/first.d.ts +10 -9
  110. package/dist/steps/first.js +9 -13
  111. package/dist/steps/graphqlResolver.d.ts +3 -76
  112. package/dist/steps/graphqlResolver.js +89 -72
  113. package/dist/steps/groupBy.d.ts +3 -2
  114. package/dist/steps/groupBy.js +1 -2
  115. package/dist/steps/index.d.ts +12 -3
  116. package/dist/steps/index.js +48 -6
  117. package/dist/steps/lambda.d.ts +9 -9
  118. package/dist/steps/lambda.js +18 -9
  119. package/dist/steps/last.d.ts +9 -8
  120. package/dist/steps/last.js +7 -6
  121. package/dist/steps/list.d.ts +20 -9
  122. package/dist/steps/list.js +56 -13
  123. package/dist/steps/listTransform.d.ts +18 -22
  124. package/dist/steps/listTransform.js +58 -27
  125. package/dist/steps/listen.d.ts +6 -11
  126. package/dist/steps/listen.js +14 -16
  127. package/dist/steps/load.d.ts +42 -33
  128. package/dist/steps/load.js +60 -27
  129. package/dist/steps/node.d.ts +17 -10
  130. package/dist/steps/node.js +18 -6
  131. package/dist/steps/object.d.ts +33 -27
  132. package/dist/steps/object.js +53 -32
  133. package/dist/steps/partitionByIndex.d.ts +3 -2
  134. package/dist/steps/partitionByIndex.js +1 -2
  135. package/dist/steps/polymorphicBranch.d.ts +27 -0
  136. package/dist/steps/polymorphicBranch.js +62 -0
  137. package/dist/steps/proxy.d.ts +11 -12
  138. package/dist/steps/proxy.js +8 -5
  139. package/dist/steps/remapKeys.d.ts +10 -10
  140. package/dist/steps/remapKeys.js +14 -7
  141. package/dist/steps/reverse.d.ts +8 -8
  142. package/dist/steps/reverse.js +10 -7
  143. package/dist/steps/setter.d.ts +9 -18
  144. package/dist/steps/setter.js +24 -14
  145. package/dist/steps/sideEffect.d.ts +28 -0
  146. package/dist/steps/sideEffect.js +44 -0
  147. package/dist/stringifyPayload.js +1 -2
  148. package/dist/stripAnsi.js +2 -2
  149. package/dist/subscribe.d.ts +7 -1
  150. package/dist/subscribe.js +24 -9
  151. package/dist/thereCanBeOnlyOne.js +1 -1
  152. package/dist/timeSource.d.ts +0 -1
  153. package/dist/utils.d.ts +56 -55
  154. package/dist/utils.js +179 -59
  155. package/dist/version.d.ts +1 -1
  156. package/dist/version.js +1 -1
  157. package/package.json +5 -8
package/dist/step.js CHANGED
@@ -1,19 +1,27 @@
1
1
  "use strict";
2
- var _a;
2
+ var _a, _b;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.assertListCapableStep = exports.isListCapableStep = exports.assertModifierStep = exports.isModifierStep = exports.ModifierStep = exports.isPolymorphicStep = exports.isStreamableStep = exports.isListLikeStep = exports.isObjectLikeStep = exports.isUnbatchedExecutableStep = exports.assertExecutableStep = exports.isExecutableStep = exports.UnbatchedExecutableStep = exports.ExecutableStep = exports.BaseStep = exports.assertFinalized = exports.$$noExec = exports.$$deepDepSkip = void 0;
4
+ exports.isExecutableStep = exports.UnbatchedExecutableStep = exports.ExecutableStep = exports.UnbatchedStep = exports.Step = exports.assertFinalized = exports.$$noExec = void 0;
5
+ exports.isStep = isStep;
6
+ exports.assertStep = assertStep;
7
+ exports.isUnbatchedStep = isUnbatchedStep;
8
+ exports.isObjectLikeStep = isObjectLikeStep;
9
+ exports.isListLikeStep = isListLikeStep;
10
+ exports.isPolymorphicStep = isPolymorphicStep;
11
+ exports.isListCapableStep = isListCapableStep;
12
+ exports.assertListCapableStep = assertListCapableStep;
13
+ exports.assertExecutableStep = assertExecutableStep;
5
14
  const tslib_1 = require("tslib");
6
15
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
16
  const tamedevil_1 = tslib_1.__importDefault(require("tamedevil"));
8
17
  const dev_js_1 = require("./dev.js");
9
18
  const withGlobalLayerPlan_js_1 = require("./engine/lib/withGlobalLayerPlan.js");
19
+ const lock_js_1 = require("./engine/lock.js");
20
+ const error_js_1 = require("./error.js");
10
21
  const global_js_1 = require("./global.js");
11
22
  const inspect_js_1 = require("./inspect.js");
12
23
  const interfaces_js_1 = require("./interfaces.js");
13
- /**
14
- * @internal
15
- */
16
- exports.$$deepDepSkip = Symbol("deepDepSkip_experimental");
24
+ const utils_js_1 = require("./utils.js");
17
25
  /**
18
26
  * This indicates that a step never executes (e.g. __ItemStep and __ValueStep)
19
27
  * and thus when executed skips direct to reallyCompletedStep.
@@ -21,6 +29,7 @@ exports.$$deepDepSkip = Symbol("deepDepSkip_experimental");
21
29
  * @internal
22
30
  */
23
31
  exports.$$noExec = Symbol("noExec");
32
+ const ref_flagError = tamedevil_1.default.ref(error_js_1.flagError, "flagError");
24
33
  function throwDestroyed() {
25
34
  let message;
26
35
  try {
@@ -32,7 +41,7 @@ function throwDestroyed() {
32
41
  throw new Error(message);
33
42
  }
34
43
  function isDeepDepSkippable($dep) {
35
- return exports.$$deepDepSkip in $dep && typeof $dep[exports.$$deepDepSkip] === "function";
44
+ return interfaces_js_1.$$deepDepSkip in $dep && typeof $dep[interfaces_js_1.$$deepDepSkip] === "function";
36
45
  }
37
46
  function reallyAssertFinalized(plan) {
38
47
  if (!plan.isFinalized) {
@@ -42,80 +51,154 @@ function reallyAssertFinalized(plan) {
42
51
  // Optimise this away in production.
43
52
  exports.assertFinalized = !dev_js_1.isDev ? dev_js_1.noop : reallyAssertFinalized;
44
53
  /**
45
- * The base abstract plan type; you should not extend this directly - instead
46
- * use an ExecutableStep (for use when planning output fields) or a
47
- * ModifierStep (for use when planning arguments/input fields).
48
- *
49
- * @remarks
50
- *
51
- * Though it might seem that ModifierStep should be used for all inputs
52
- * (arguments, input objects), this is not the case. When planning an output
53
- * field, all inputs to it (including arguments, parent plan, context, etc)
54
- * should be other executable plans. The only time that ModifierStep is used is
55
- * when writing specific plans as part of the argument or input field
56
- * definitions themself; even in these cases the inputs to these plan resolvers
57
- * will be ExecutablePlans.
54
+ * Executable plans are the plans associated with leaves on the GraphQL tree,
55
+ * they must be able to execute to return values.
58
56
  */
59
- class BaseStep {
60
- static { _a = interfaces_js_1.$$subroutine; }
57
+ class Step {
58
+ static { _a = interfaces_js_1.$$subroutine, _b = lock_js_1.$$unlock; }
61
59
  constructor() {
62
60
  this[_a] = null;
61
+ /**
62
+ * For input values, set `true` if it comes from variables/arguments since
63
+ * they cannot be modified (even by mutations), set `false` otherwise.
64
+ *
65
+ * @internal
66
+ */
67
+ this._isImmutable = false;
68
+ /** @internal */
69
+ this[_b] = undefined;
70
+ /**
71
+ * (default = ALL_FLAGS & ~FLAG_NULL)
72
+ */
73
+ this.defaultForbiddenFlags = interfaces_js_1.DEFAULT_FORBIDDEN_FLAGS;
74
+ /**
75
+ * If the peerKey of two steps do not match, then they are definitely not
76
+ * peers. Use this to reduce the load on deduplicate by more quickly
77
+ * eradicating definitely-not-peers.
78
+ *
79
+ * Note: we may well change this to be a function in future, so it's advised
80
+ * that you don't use this unless you're working inside the graphile/crystal
81
+ * core codebase.
82
+ *
83
+ * @experimental
84
+ */
85
+ this.peerKey = null;
63
86
  this.isArgumentsFinalized = false;
64
87
  this.isFinalized = false;
65
88
  this.debug = (0, global_js_1.getDebug)();
66
- this.hasSideEffects = false;
67
89
  const layerPlan = (0, withGlobalLayerPlan_js_1.currentLayerPlan)();
68
90
  this.layerPlan = layerPlan;
69
91
  this.operationPlan = layerPlan.operationPlan;
70
- }
71
- toString() {
72
- const meta = this.toStringMeta();
73
- return chalk_1.default.bold.blue(`${this.constructor.name.replace(/Step$/, "")}${meta != null && meta.length ? chalk_1.default.grey(`<${meta}>`) : ""}`);
74
- }
75
- /**
76
- * This metadata will be merged into toString when referencing this plan.
77
- */
78
- toStringMeta() {
79
- return null;
80
- }
81
- planJSONExtra() {
82
- return undefined;
83
- }
84
- finalize() {
85
- if (!this.isFinalized) {
86
- this.isFinalized = true;
87
- }
88
- else {
89
- throw new Error(`Step ${this} has already been finalized - do not call \`finalize()\` from user code!`);
90
- }
91
- }
92
- destroy() { }
93
- }
94
- exports.BaseStep = BaseStep;
95
- /**
96
- * Executable plans are the plans associated with leaves on the GraphQL tree,
97
- * they must be able to execute to return values.
98
- */
99
- class ExecutableStep extends BaseStep {
100
- constructor() {
101
- super();
92
+ this._isUnary = true;
93
+ this._isUnaryLocked = false;
94
+ this.implicitSideEffectStep = null;
95
+ this.hasSideEffects ??= false;
96
+ let hasSideEffects = false;
97
+ const stepTracker = this.layerPlan.operationPlan.stepTracker;
98
+ Object.defineProperty(this, "hasSideEffects", {
99
+ get() {
100
+ return hasSideEffects;
101
+ },
102
+ set(value) {
103
+ /**
104
+ * If steps were created after this step, an this step doesn't depend
105
+ * on them, then it's no longer safe to change hasSideEffects.
106
+ */
107
+ let nonDependentSteps = null;
108
+ const maxStepId = stepTracker.stepCount - 1;
109
+ if (this.id === maxStepId) {
110
+ // All good - no more steps were created
111
+ }
112
+ else {
113
+ // If the step created them during initialization and is dependent on
114
+ // them, that's fine too.
115
+ for (let id = this.id + 1; id <= maxStepId; id++) {
116
+ const step = stepTracker.getStepById(id);
117
+ if ((0, utils_js_1.stepADependsOnStepB)(this, step))
118
+ continue;
119
+ if (nonDependentSteps === null) {
120
+ nonDependentSteps = [step];
121
+ }
122
+ else {
123
+ nonDependentSteps.push(step);
124
+ }
125
+ }
126
+ }
127
+ if (nonDependentSteps === null) {
128
+ hasSideEffects = value;
129
+ if (value === true) {
130
+ this.layerPlan.latestSideEffectStep = this;
131
+ this.operationPlan.resetCache();
132
+ }
133
+ else if (value !== true && hasSideEffects === true) {
134
+ throw new Error(`Cannot mark ${this} as having no side effects after having set it to have side effects.`);
135
+ }
136
+ }
137
+ else {
138
+ throw new Error(`Attempted to mark ${this} as having side effects, but other non-dependent steps (${nonDependentSteps
139
+ .map(String)
140
+ .join(", ")}) have already been created.`);
141
+ }
142
+ },
143
+ enumerable: true,
144
+ configurable: false,
145
+ });
102
146
  this.dependencies = [];
147
+ this.dependencyForbiddenFlags = [];
148
+ this.dependencyOnReject = [];
103
149
  this.dependents = [];
104
150
  this.isOptimized = false;
105
151
  this.allowMultipleOptimizations = false;
106
- this._stepOptions = { stream: null };
152
+ this._stepOptions = { stream: null, walkIterable: false };
107
153
  this.store = true;
108
154
  this.polymorphicPaths = (0, withGlobalLayerPlan_js_1.currentPolymorphicPaths)();
155
+ // Important: MUST come after `this.layerPlan = ...`
109
156
  this.id = this.layerPlan._addStep(this);
110
157
  }
158
+ /**
159
+ * Generally you should only use this once the dependencies of a step are
160
+ * established, if you use it beforehand and it returns `true` then adding a
161
+ * non-unary dependency later will result in an error.
162
+ */
163
+ getAndFreezeIsUnary() {
164
+ this._isUnaryLocked = true;
165
+ return this._isUnary;
166
+ }
111
167
  withMyLayerPlan(callback) {
112
168
  return (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.layerPlan, this.polymorphicPaths, callback);
113
169
  }
170
+ /** @experimental */
171
+ withLayerPlan(callback) {
172
+ return (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.layerPlan, this.polymorphicPaths, callback);
173
+ }
114
174
  getStep(id) {
115
175
  return this.layerPlan.getStep(id, this);
116
176
  }
117
- getDep(depId) {
118
- return this.dependencies[depId];
177
+ getDepOptions(depId) {
178
+ const step = this.dependencies[depId];
179
+ const forbiddenFlags = this.dependencyForbiddenFlags[depId];
180
+ const onReject = this.dependencyOnReject[depId];
181
+ const acceptFlags = interfaces_js_1.ALL_FLAGS & ~forbiddenFlags;
182
+ return { step, acceptFlags, onReject };
183
+ }
184
+ getDep(_depId, _throwOnFlagged = false) {
185
+ // This gets replaced when `__FlagStep` is loaded. Were we on ESM we could
186
+ // just put the code here, but since we're not we have to avoid the
187
+ // circular dependency.
188
+ throw new Error(`Grafast failed to load correctly`);
189
+ }
190
+ maybeGetDep(depId, throwOnFlagged = false) {
191
+ return depId == null
192
+ ? null
193
+ : throwOnFlagged
194
+ ? this.getDep(depId, true)
195
+ : this.getDep(depId);
196
+ }
197
+ getDepOrConstant(_depId, _fallback) {
198
+ // This gets replaced when `constant` is loaded. Were we on ESM we could
199
+ // just put the code here, but since we're not we have to avoid the
200
+ // circular dependency.
201
+ throw new Error(`Grafast failed to load correctly`);
119
202
  }
120
203
  /**
121
204
  * Like getDep, except it skips over __ItemStep and similar steps to get to
@@ -129,48 +212,72 @@ class ExecutableStep extends BaseStep {
129
212
  // __ItemStep and __ListTransformStep are safe to walk through, but other
130
213
  // classes may not be.
131
214
  while (isDeepDepSkippable($dep)) {
132
- $dep = $dep[exports.$$deepDepSkip]();
215
+ $dep = $dep[interfaces_js_1.$$deepDepSkip]();
133
216
  }
134
217
  return $dep;
135
218
  }
136
- toString() {
137
- const meta = this.toStringMeta();
138
- return chalk_1.default.bold.blue(`${this.constructor.name.replace(/Step$/, "")}${this.layerPlan.id === 0 ? "" : chalk_1.default.grey(`{${this.layerPlan.id}}`)}${meta != null && meta.length ? chalk_1.default.grey(`<${meta}>`) : ""}[${(0, inspect_js_1.inspect)(this.id, {
139
- colors: true,
140
- })}]`);
219
+ /**
220
+ * Cache a generated step by a given identifier (cacheKey) such that we don't
221
+ * need to regenerate it on future calls, significantly reducing the load on
222
+ * deduplication later.
223
+ *
224
+ * @experimental
225
+ */
226
+ cacheStep(actionKey, cacheKey, cb) {
227
+ return this.operationPlan.cacheStep(this, actionKey, cacheKey, cb);
141
228
  }
142
- addDependency(step, skipDeduplication = false) {
143
- if (this.isFinalized) {
144
- throw new Error("You cannot add a dependency after the step is finalized.");
229
+ toString() {
230
+ let meta;
231
+ try {
232
+ // If we log out too early, the meta function might fail.
233
+ meta = this.toStringMeta();
145
234
  }
146
- if (!(step instanceof ExecutableStep)) {
147
- throw new Error(`Error occurred when adding dependency for '${this}', value passed was not a step, it was '${(0, inspect_js_1.inspect)(step)}'`);
235
+ catch (e) {
236
+ // Ignore
148
237
  }
149
- if (dev_js_1.isDev) {
150
- // Check that we can actually add this as a dependency
151
- if (!this.layerPlan.ancestry.includes(step.layerPlan)) {
152
- throw new Error(
153
- //console.error(
154
- // This is not a GrafastInternalError
155
- `Attempted to add '${step}' (${step.layerPlan}) as a dependency of '${this}' (${this.layerPlan}), but we cannot because that LayerPlan isn't an ancestor`);
156
- }
238
+ return chalk_1.default.bold.blue(`${this.constructor.name.replace(/Step$/, "")}${this.layerPlan.id === 0 ? "" : chalk_1.default.grey(`{${this.layerPlan.id}}`)}${this._isUnary ? "➊" : ""}${this._stepOptions.stream != null ? "@s" : ""}${meta != null && meta.length ? chalk_1.default.grey(`<${meta}>`) : ""}[${(0, inspect_js_1.inspect)(this.id, { colors: true })}]`);
239
+ }
240
+ /**
241
+ * This metadata will be merged into toString when referencing this plan.
242
+ */
243
+ toStringMeta() {
244
+ return null;
245
+ }
246
+ planJSONExtra() {
247
+ return undefined;
248
+ }
249
+ canAddDependency(step) {
250
+ return (0, utils_js_1.stepAMayDependOnStepB)(this, step);
251
+ }
252
+ addDependency(stepOrOptions) {
253
+ const options = stepOrOptions instanceof Step
254
+ ? { step: stepOrOptions, skipDeduplication: false }
255
+ : stepOrOptions;
256
+ if (options.step.layerPlan.id > this.layerPlan.id) {
257
+ throw new Error(`Cannot add dependency ${options.step} to ${this} since the former is in a deeper layerPlan (creates a catch-22)`);
157
258
  }
158
- // When copying dependencies between classes, we might not want to
159
- // deduplicate because we might refer to the dependency by its index. As
160
- // such, we should only dedupe by default but allow opting out.
161
- if (!skipDeduplication) {
162
- const existingIndex = this.dependencies.indexOf(step);
163
- if (existingIndex >= 0) {
164
- return existingIndex;
165
- }
259
+ return this.operationPlan.stepTracker.addStepDependency(this, options);
260
+ }
261
+ /**
262
+ * Adds "unary" dependencies; in `execute({count, values})` you'll receive a
263
+ * `values[index]` (where `index` is the return value of this function) with
264
+ * `isBatch = false` so you can use the `values[index].value` property
265
+ * directly.
266
+ */
267
+ addUnaryDependency(stepOrOptions) {
268
+ const options = stepOrOptions instanceof Step ? { step: stepOrOptions } : stepOrOptions;
269
+ if (options.step.layerPlan.id > this.layerPlan.id) {
270
+ throw new Error(`Cannot add dependency ${options.step} to ${this} since the former is in a deeper layerPlan (creates a catch-22)`);
166
271
  }
167
- return this.operationPlan.stepTracker.addStepDependency(this, step);
272
+ return this.operationPlan.stepTracker.addStepUnaryDependency(this, options);
168
273
  }
169
274
  finalize() {
170
275
  if (typeof this.isSyncAndSafe !== "boolean") {
171
276
  // Take a guess
172
- const isAsync = this.execute.constructor.name === "AsyncFunction";
173
- if (isAsync) {
277
+ if (!dev_js_1.isDev) {
278
+ this.isSyncAndSafe = false;
279
+ }
280
+ else if (this.execute.constructor.name === "AsyncFunction") {
174
281
  this.isSyncAndSafe = false;
175
282
  }
176
283
  else {
@@ -182,17 +289,29 @@ class ExecutableStep extends BaseStep {
182
289
  this.execute.constructor.name === "AsyncFunction") {
183
290
  throw new Error(`${this} claims to be synchronous, however the execute method is asynchronous`);
184
291
  }
185
- super.finalize();
292
+ if (!this.isFinalized) {
293
+ this.isFinalized = true;
294
+ }
295
+ else {
296
+ throw new Error(`Step ${this} has already been finalized - do not call \`finalize()\` from user code!`);
297
+ }
186
298
  }
187
299
  /**
188
- * This function will be called with a `values` list: an array of entries for
189
- * each incoming grafast object, where each entry in the array is a list of
190
- * the values retrieved from executing the plans in `this.dependencies` for
191
- * that grafast object.
300
+ * This function will be called with 'execution details', an object containing:
192
301
  *
193
- * It must return a list with the same length as `values`, where each value
194
- * in the list relates to the result of executing this plan for the
195
- * corresponding entry in the `values` list.
302
+ * - `count`: the number of entries in the batch that's being executed
303
+ * - `values`: a tuple representing the runtime values of the steps
304
+ * dependencies; each value in the tuple is an object, either a batch object
305
+ * containing a list of size `count` containing the values, or a unary
306
+ * object containing the single value common to all entries.
307
+ * - `indexMap`: helper function to map over each index from `0` to `count-1`,
308
+ * returning the resulting array.
309
+ * - `indexForEach`: as `indexMap`, but without the array result.
310
+ * - `meta`: [experimental]
311
+ *
312
+ * `execute` must return a list with `count` entries, where each value in the
313
+ * list relates to the result of executing this plan for the corresponding
314
+ * entry in each of the entries in the `values` tuple.
196
315
  *
197
316
  * IMPORTANT: it is up to the execute function to cache/memoize results as
198
317
  * appropriate for performance, this can be done via the `meta` object.
@@ -202,13 +321,10 @@ class ExecutableStep extends BaseStep {
202
321
  * add attributes to meta for each purpose (e.g. use `meta.cache` for
203
322
  * memoizing results) so that you can expand your usage of meta in future.
204
323
  */
205
- /* abstract */ execute(count, values,
206
- // EXPERIMENTAL
207
- extra) {
324
+ /* abstract */
325
+ execute(details) {
208
326
  // ESLint/TS: ignore not used.
209
- count;
210
- values;
211
- extra;
327
+ details;
212
328
  throw new Error(`${this} has not implemented an 'execute' method`);
213
329
  }
214
330
  destroy() {
@@ -221,14 +337,14 @@ class ExecutableStep extends BaseStep {
221
337
  this.optimize = throwDestroyed;
222
338
  this.finalize = throwDestroyed;
223
339
  this.execute = throwDestroyed;
224
- super.destroy();
225
340
  }
226
341
  }
227
- exports.ExecutableStep = ExecutableStep;
228
- function _buildOptimizedExecuteExpression(depCount, isSyncAndSafe) {
229
- const depIndexes = [];
342
+ exports.Step = Step;
343
+ exports.ExecutableStep = Step;
344
+ function _buildOptimizedExecuteV2Expression(depCount, isSyncAndSafe) {
345
+ const identifiers = [];
230
346
  for (let i = 0; i < depCount; i++) {
231
- depIndexes.push(i);
347
+ identifiers.push(tamedevil_1.default.identifier(`value${i}`));
232
348
  }
233
349
  const tryOrNot = (inFrag) => {
234
350
  if (isSyncAndSafe) {
@@ -239,20 +355,21 @@ function _buildOptimizedExecuteExpression(depCount, isSyncAndSafe) {
239
355
  try {
240
356
  ${tamedevil_1.default.indent(inFrag)}
241
357
  } catch (e) {
242
- results[i] = e instanceof Error ? e : Promise.reject(e);
358
+ results[i] = ${ref_flagError}(e);
243
359
  }\
244
360
  `;
245
361
  }
246
362
  };
247
363
  return (0, tamedevil_1.default) `\
248
- (function execute(count, values, extra) {
249
- const [
250
- ${tamedevil_1.default.join(depIndexes.map((i) => (0, tamedevil_1.default) ` ${tamedevil_1.default.identifier(`list${i}`)},\n`), "")}\
251
- ] = values;
364
+ (function execute({
365
+ count,
366
+ values: [${tamedevil_1.default.join(identifiers, ", ")}],
367
+ extra,
368
+ }) {
252
369
  const results = [];
253
370
  for (let i = 0; i < count; i++) {
254
371
  ${tryOrNot((0, tamedevil_1.default) `\
255
- results[i] = this.unbatchedExecute(extra, ${tamedevil_1.default.join(depIndexes.map((depIndex) => (0, tamedevil_1.default) `${tamedevil_1.default.identifier(`list${depIndex}`)}[i]`), ", ")});\
372
+ results[i] = this.unbatchedExecute(extra, ${tamedevil_1.default.join(identifiers.map((identifier) => (0, tamedevil_1.default) `${identifier}.at(i)`), ", ")});\
256
373
  `)}
257
374
  }
258
375
  return results;
@@ -264,11 +381,11 @@ const safeCache = [];
264
381
  tamedevil_1.default.batch(() => {
265
382
  for (let i = 0; i <= MAX_DEPENDENCIES_TO_CACHE; i++) {
266
383
  const depCount = i;
267
- const unsafeExpression = _buildOptimizedExecuteExpression(depCount, false);
384
+ const unsafeExpression = _buildOptimizedExecuteV2Expression(depCount, false);
268
385
  tamedevil_1.default.runInBatch(unsafeExpression, (fn) => {
269
386
  unsafeCache[depCount] = fn;
270
387
  });
271
- const safeExpression = _buildOptimizedExecuteExpression(depCount, true);
388
+ const safeExpression = _buildOptimizedExecuteV2Expression(depCount, true);
272
389
  tamedevil_1.default.runInBatch(safeExpression, (fn) => {
273
390
  safeCache[depCount] = fn;
274
391
  });
@@ -282,115 +399,77 @@ function buildOptimizedExecute(depCount, isSyncAndSafe, callback) {
282
399
  return;
283
400
  }
284
401
  // Build it
285
- const expression = _buildOptimizedExecuteExpression(depCount, isSyncAndSafe);
402
+ const expression = _buildOptimizedExecuteV2Expression(depCount, isSyncAndSafe);
286
403
  tamedevil_1.default.runInBatch(expression, (fn) => {
287
404
  callback(fn);
288
405
  });
289
406
  }
290
- class UnbatchedExecutableStep extends ExecutableStep {
407
+ class UnbatchedStep extends Step {
291
408
  static { this.$$export = {
292
409
  moduleName: "grafast",
293
- exportName: "UnbatchedExecutableStep",
410
+ exportName: "UnbatchedStep",
294
411
  }; }
295
412
  finalize() {
296
- // If they've not replaced 'execute', use our optimized form
297
- if (this.execute === UnbatchedExecutableStep.prototype.execute) {
413
+ if (this.execute === UnbatchedStep.prototype.execute) {
414
+ // If they've not replaced 'execute', use our optimized form
298
415
  buildOptimizedExecute(this.dependencies.length, this.isSyncAndSafe, (fn) => {
299
416
  this.execute = fn;
300
417
  });
301
418
  }
302
419
  super.finalize();
303
420
  }
304
- execute(count, values, extra) {
421
+ execute({ indexMap, values, extra, }) {
305
422
  console.warn(`${this} didn't call 'super.finalize()' in the finalize method.`);
306
- const results = [];
307
- for (let i = 0; i < count; i++) {
423
+ const depCount = this.dependencies.length;
424
+ return indexMap((i) => {
308
425
  try {
309
- const tuple = values.map((list) => list[i]);
310
- results[i] = this.unbatchedExecute(extra, ...tuple);
426
+ const tuple = [];
427
+ for (let j = 0; j < depCount; j++) {
428
+ tuple[j] = values[j].at(i);
429
+ }
430
+ return this.unbatchedExecute(extra, ...tuple);
311
431
  }
312
432
  catch (e) {
313
- results[i] = e instanceof Error ? e : Promise.reject(e);
433
+ return (0, error_js_1.flagError)(e);
314
434
  }
315
- }
316
- return results;
435
+ });
317
436
  }
318
437
  }
319
- exports.UnbatchedExecutableStep = UnbatchedExecutableStep;
320
- function isExecutableStep(step) {
321
- return (step instanceof BaseStep &&
322
- "execute" in step &&
323
- typeof step.execute === "function");
438
+ exports.UnbatchedStep = UnbatchedStep;
439
+ exports.UnbatchedExecutableStep = UnbatchedStep;
440
+ function isStep(step) {
441
+ return step instanceof Step;
324
442
  }
325
- exports.isExecutableStep = isExecutableStep;
326
- function assertExecutableStep(step) {
327
- if (!isExecutableStep(step)) {
328
- throw new Error(`Expected an executable step, but received something else: ${(0, inspect_js_1.inspect)(step)}`);
443
+ function assertStep(step) {
444
+ if (!isStep(step)) {
445
+ throw new Error(`Expected a step, but received something else: ${(0, inspect_js_1.inspect)(step)}`);
329
446
  }
330
447
  }
331
- exports.assertExecutableStep = assertExecutableStep;
332
- function isUnbatchedExecutableStep(step) {
333
- return (isExecutableStep(step) &&
334
- typeof step.unbatchedExecute === "function");
448
+ function isUnbatchedStep(step) {
449
+ return isStep(step) && typeof step.unbatchedExecute === "function";
335
450
  }
336
- exports.isUnbatchedExecutableStep = isUnbatchedExecutableStep;
337
451
  function isObjectLikeStep(plan) {
338
452
  return "get" in plan && typeof plan.get === "function";
339
453
  }
340
- exports.isObjectLikeStep = isObjectLikeStep;
341
454
  function isListLikeStep(plan) {
342
455
  return "at" in plan && typeof plan.at === "function";
343
456
  }
344
- exports.isListLikeStep = isListLikeStep;
345
- function isStreamableStep(plan) {
346
- return typeof plan.stream === "function";
347
- }
348
- exports.isStreamableStep = isStreamableStep;
349
457
  function isPolymorphicStep(s) {
350
458
  return ("planForType" in s &&
351
459
  typeof s.planForType === "function");
352
460
  }
353
- exports.isPolymorphicStep = isPolymorphicStep;
354
- /**
355
- * Modifier plans modify their parent plan (which may be another ModifierStep
356
- * or an ExecutableStep). First they gather all the requirements from their
357
- * children (if any) being applied to them, then they apply themselves to their
358
- * parent plan. This application is done through the `apply()` method.
359
- *
360
- * Modifier plans do not use dependencies.
361
- */
362
- class ModifierStep extends BaseStep {
363
- constructor($parent) {
364
- super();
365
- this.$parent = $parent;
366
- this.id = this.layerPlan._addModifierStep(this);
367
- }
368
- toString() {
369
- const meta = this.toStringMeta();
370
- return chalk_1.default.bold.blue(`${this.constructor.name.replace(/Step$/, "")}${meta != null && meta.length ? chalk_1.default.grey(`<${meta}>`) : ""}[${(0, inspect_js_1.inspect)(this.id, {
371
- colors: true,
372
- })}]`);
373
- }
374
- }
375
- exports.ModifierStep = ModifierStep;
376
- function isModifierStep(plan) {
377
- return "apply" in plan && typeof plan.apply === "function";
378
- }
379
- exports.isModifierStep = isModifierStep;
380
- function assertModifierStep(plan, pathDescription) {
381
- if (!isModifierStep(plan)) {
382
- throw new Error(`The plan returned from '${pathDescription}' should be a modifier plan, but it does not implement the 'apply' method.`);
383
- }
384
- }
385
- exports.assertModifierStep = assertModifierStep;
386
461
  function isListCapableStep(plan) {
387
462
  return "listItem" in plan && typeof plan.listItem === "function";
388
463
  }
389
- exports.isListCapableStep = isListCapableStep;
390
464
  function assertListCapableStep(plan, pathDescription) {
391
465
  if (!isListCapableStep(plan)) {
392
466
  throw new Error(`The plan returned from '${pathDescription}' should be a list capable plan, but ${plan} does not implement the 'listItem' method.`);
393
467
  }
394
468
  }
395
- exports.assertListCapableStep = assertListCapableStep;
469
+ /** @deprecated Use isStep instead */
470
+ exports.isExecutableStep = isStep;
471
+ /** @deprecated Use isStep instead */
472
+ function assertExecutableStep(step) {
473
+ return assertStep(step);
474
+ }
396
475
  //# sourceMappingURL=step.js.map