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
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.newBucket = exports.executeBucket = void 0;
3
+ exports.executeBucket = executeBucket;
4
+ exports.newBucket = newBucket;
5
+ exports.bucketToString = bucketToString;
6
+ exports.batchExecutionValue = batchExecutionValue;
7
+ exports.unaryExecutionValue = unaryExecutionValue;
4
8
  const tslib_1 = require("tslib");
5
9
  const iterall_1 = require("iterall");
6
10
  const assert = tslib_1.__importStar(require("../assert.js"));
@@ -8,17 +12,8 @@ const dev_js_1 = require("../dev.js");
8
12
  const error_js_1 = require("../error.js");
9
13
  const inspect_js_1 = require("../inspect.js");
10
14
  const interfaces_js_1 = require("../interfaces.js");
11
- const step_js_1 = require("../step.js");
12
15
  const timeSource_js_1 = require("../timeSource.js");
13
16
  const utils_js_1 = require("../utils.js");
14
- const DEBUG_POLYMORPHISM = false;
15
- /** Path to use when there's no polymorphic paths. */
16
- const NO_POLY_PATH = "";
17
- // TODO: the handling of polymorphism via POLY_SKIPPED is distasteful. Find a
18
- // better approach.
19
- // An error that indicates this entry was skipped because it didn't match
20
- // polymorphicPath.
21
- const POLY_SKIPPED = (0, error_js_1.newGrafastError)(new Error("GrafastInternalError<757b99f9-cb4d-4141-895d-8c687b2048fd>: Polymorphic skipped; you should never see this"), null);
22
17
  const timeoutError = Object.freeze(new error_js_1.SafeError("Execution timeout exceeded, please simplify or add limits to your request.", Object.freeze({ [interfaces_js_1.$$timeout]: true })));
23
18
  function noop() {
24
19
  /*noop*/
@@ -33,31 +28,45 @@ function noop() {
33
28
  *
34
29
  * @internal
35
30
  */
36
- function mergeErrorsBackIn(results, errors, resultCount) {
31
+ function mergeErrorsBackIn(results, forcedValues, resultCount) {
32
+ const finalFlags = [];
37
33
  const finalResults = [];
34
+ /** The index within `results`, which is shorter than `resultCount` */
38
35
  let resultIndex = 0;
39
- for (let i = 0; i < resultCount; i++) {
40
- const error = errors[i];
41
- if (error !== undefined) {
42
- finalResults[i] = error;
36
+ for (let finalIndex = 0; finalIndex < resultCount; finalIndex++) {
37
+ const flags = forcedValues.flags[finalIndex];
38
+ if (flags !== undefined) {
39
+ const value = forcedValues.results[finalIndex];
40
+ finalResults[finalIndex] = value;
41
+ finalFlags[finalIndex] = flags;
43
42
  }
44
43
  else {
45
- finalResults[i] = results[resultIndex++];
44
+ finalResults[finalIndex] = results[resultIndex++];
45
+ finalFlags[finalIndex] = 0;
46
46
  }
47
47
  }
48
- return finalResults;
48
+ return { flags: finalFlags, results: finalResults };
49
49
  }
50
50
  /** @internal */
51
51
  function executeBucket(bucket, requestContext) {
52
52
  /**
53
53
  * Execute the step directly; since there's no errors we can pass the
54
- * dependencies through verbatim!
54
+ * dependencies through verbatim.
55
55
  */
56
- const reallyExecuteStepWithNoErrors = executeOrStream;
57
- const { stopTime, eventEmitter } = requestContext;
56
+ function reallyExecuteStepWithoutFiltering(size, step, dependencies, extra) {
57
+ const results = executeOrStream(size, step, dependencies, extra);
58
+ const flags = (0, utils_js_1.arrayOfLength)(size, interfaces_js_1.NO_FLAGS);
59
+ if ((0, utils_js_1.isPromiseLike)(results)) {
60
+ return results.then((results) => ({ flags, results }));
61
+ }
62
+ else {
63
+ return { flags, results };
64
+ }
65
+ }
66
+ const { stopTime, eventEmitter, args } = requestContext;
67
+ const { middleware } = args;
58
68
  const { metaByMetaKey, size, store, layerPlan: { phases, children: childLayerPlans }, } = bucket;
59
69
  const phaseCount = phases.length;
60
- let sideEffectStepsWithErrors = null;
61
70
  // Like a `for(i = 0; i < phaseCount; i++)` loop with some `await`s in it, except it does promise
62
71
  // handling manually so that it can complete synchronously (no promises) if
63
72
  // possible.
@@ -74,9 +83,8 @@ function executeBucket(bucket, requestContext) {
74
83
  */
75
84
  let indexesPendingLoopOver = [];
76
85
  let executePromises = null;
77
- let sideEffectSteps = null;
78
86
  let executePromiseResultIndex = null;
79
- const results = [];
87
+ const resultList = [];
80
88
  if (phase.checkTimeout &&
81
89
  stopTime !== null &&
82
90
  timeSource_js_1.timeSource.now() >= stopTime) {
@@ -85,11 +93,14 @@ function executeBucket(bucket, requestContext) {
85
93
  const normalSteps = phase.normalSteps;
86
94
  for (let normalStepIndex = 0, l = normalSteps.length; normalStepIndex < l; normalStepIndex++) {
87
95
  const step = normalSteps[normalStepIndex].step;
88
- const r = (0, error_js_1.newGrafastError)(timeoutError, step.id);
89
- const result = (0, utils_js_1.arrayOfLength)(bucket.size, r);
90
- results[normalStepIndex] = result;
96
+ const stepSize = step._isUnary ? 1 : bucket.size;
97
+ const r = timeoutError;
98
+ const results = (0, utils_js_1.arrayOfLength)(stepSize, r);
99
+ const flags = (0, utils_js_1.arrayOfLength)(stepSize, interfaces_js_1.FLAG_ERROR);
100
+ resultList[normalStepIndex] = { flags, results };
91
101
  indexesPendingLoopOver.push(normalStepIndex);
92
- bucket.hasErrors = true;
102
+ // TODO: I believe we can remove this line?
103
+ bucket.flagUnion |= interfaces_js_1.FLAG_ERROR;
93
104
  }
94
105
  }
95
106
  }
@@ -97,18 +108,11 @@ function executeBucket(bucket, requestContext) {
97
108
  const normalSteps = phase.normalSteps;
98
109
  for (let normalStepIndex = 0, l = normalSteps.length; normalStepIndex < l; normalStepIndex++) {
99
110
  const step = normalSteps[normalStepIndex].step;
100
- if (step.hasSideEffects) {
101
- if (sideEffectSteps === null) {
102
- sideEffectSteps = [step];
103
- }
104
- else {
105
- sideEffectSteps.push(step);
106
- }
107
- }
111
+ const stepSize = step._isUnary ? 1 : bucket.size;
108
112
  try {
109
113
  const r = executeStep(step);
110
114
  if ((0, utils_js_1.isPromiseLike)(r)) {
111
- results[normalStepIndex] = undefined /* will populate shortly */;
115
+ resultList[normalStepIndex] = undefined /* will populate shortly */;
112
116
  if (!executePromises) {
113
117
  executePromises = [r];
114
118
  executePromiseResultIndex = [normalStepIndex];
@@ -119,37 +123,21 @@ function executeBucket(bucket, requestContext) {
119
123
  }
120
124
  }
121
125
  else {
122
- results[normalStepIndex] = r;
126
+ resultList[normalStepIndex] = r;
123
127
  indexesPendingLoopOver.push(normalStepIndex);
124
128
  }
125
129
  }
126
130
  catch (e) {
127
- const r = (0, error_js_1.newGrafastError)(e, step.id);
128
- const result = (0, utils_js_1.arrayOfLength)(bucket.size, r);
129
- results[normalStepIndex] = result;
131
+ const r = e;
132
+ const results = (0, utils_js_1.arrayOfLength)(stepSize, r);
133
+ const flags = (0, utils_js_1.arrayOfLength)(stepSize, interfaces_js_1.FLAG_ERROR);
134
+ resultList[normalStepIndex] = { flags, results };
130
135
  indexesPendingLoopOver.push(normalStepIndex);
131
- bucket.hasErrors = true;
136
+ // TODO: I believe we can remove this line?
137
+ bucket.flagUnion |= interfaces_js_1.FLAG_ERROR;
132
138
  }
133
139
  }
134
140
  }
135
- const handleSideEffectSteps = () => {
136
- if (!sideEffectStepsWithErrors) {
137
- sideEffectStepsWithErrors = bucket.polymorphicPathList.reduce((memo, path) => {
138
- memo[path ?? NO_POLY_PATH] = [];
139
- return memo;
140
- }, Object.create(null));
141
- }
142
- for (const step of sideEffectSteps) {
143
- if (step.polymorphicPaths) {
144
- for (const path of step.polymorphicPaths) {
145
- sideEffectStepsWithErrors[path].push(step);
146
- }
147
- }
148
- else {
149
- sideEffectStepsWithErrors[NO_POLY_PATH].push(step);
150
- }
151
- }
152
- };
153
141
  const next = () => {
154
142
  return nextPhase(phaseIndex + 1);
155
143
  };
@@ -164,16 +152,26 @@ function executeBucket(bucket, requestContext) {
164
152
  indexesPendingLoopOver = [];
165
153
  // Validate executed steps
166
154
  for (const allStepsIndex of indexesToProcess) {
167
- const result = results[allStepsIndex];
168
155
  const finishedStep = _allSteps[allStepsIndex];
169
- const resultLength = result?.length;
170
- if (resultLength !== size) {
171
- if (!Array.isArray(result)) {
172
- throw new Error(`Result from ${finishedStep} should be an array, instead received ${(0, inspect_js_1.inspect)(result, { colors: true })}`);
156
+ const internalResult = resultList[allStepsIndex];
157
+ if (!internalResult) {
158
+ throw new Error(`GrafastInternalError<166ef53d-b80d-4bea-8b54-803c2694112a>: Result from ${finishedStep} should exist`);
159
+ }
160
+ const { /* flags, */ results } = internalResult;
161
+ const resultLength = results?.length;
162
+ const expectedSize = finishedStep._isUnary ? 1 : size;
163
+ if (resultLength !== expectedSize) {
164
+ if (!Array.isArray(results)) {
165
+ throw new Error(`Result from ${finishedStep} should be an array, instead received ${(0, inspect_js_1.inspect)(results, { colors: true })}`);
173
166
  }
174
- throw new Error(`Result array from ${finishedStep} should have length ${size}, instead it had length ${result.length}`);
167
+ throw new Error(`Result array from ${finishedStep} should have length ${expectedSize}${finishedStep._isUnary ? " (because it's unary)" : ""}, instead it had length ${results.length}`);
168
+ }
169
+ if (finishedStep._isUnary) {
170
+ // Handled later
171
+ }
172
+ else {
173
+ bucket.store.set(finishedStep.id, batchExecutionValue((0, utils_js_1.arrayOfLength)(size)));
175
174
  }
176
- bucket.store.set(finishedStep.id, (0, utils_js_1.arrayOfLength)(size));
177
175
  }
178
176
  // Need to complete promises, check for errors, etc.
179
177
  // **DO NOT THROW, DO NOT ALLOW AN ERROR TO BE RAISED!**
@@ -182,25 +180,34 @@ function executeBucket(bucket, requestContext) {
182
180
  let promises;
183
181
  let pendingPromises;
184
182
  let pendingPromiseIndexes;
185
- const success = (finishedStep, finalResult, resultIndex, value) => {
186
- let proto;
187
- if (
188
- // Fast-lane for non-objects
189
- typeof value !== "object" ||
190
- value === null) {
191
- finalResult[resultIndex] = value;
183
+ // TODO: it seems that if this throws an error it results in a permanent
184
+ // hang of defers? In the mean time... Don't throw any errors here!
185
+ const success = (finishedStep, bucket, resultIndex, value, flags) => {
186
+ // Fast lanes
187
+ if (typeof value !== "object") {
188
+ // non-objects
189
+ bucket.setResult(finishedStep, resultIndex, value, flags);
190
+ return;
191
+ }
192
+ else if (value === null) {
193
+ // nulls
194
+ const finalFlags = flags | interfaces_js_1.FLAG_NULL;
195
+ bucket.setResult(finishedStep, resultIndex, null, finalFlags);
196
+ return;
197
+ }
198
+ else if ((0, error_js_1.isFlaggedValue)(value)) {
199
+ // flagged values
200
+ const finalFlags = flags | value.flags;
201
+ bucket.setResult(finishedStep, resultIndex, value.value, finalFlags);
192
202
  return;
193
203
  }
194
204
  let valueIsAsyncIterable;
195
- if (
196
- // Are we streaming this? If so, we need an iterable or async
197
- // iterable.
198
- finishedStep._stepOptions.stream &&
205
+ if (finishedStep._stepOptions.walkIterable &&
206
+ // PERF: do we want to handle arrays differently?
199
207
  ((valueIsAsyncIterable = (0, iterall_1.isAsyncIterable)(value)) || (0, iterall_1.isIterable)(value))) {
200
- const streamOptions = finishedStep._stepOptions.stream;
201
- const initialCount = streamOptions
202
- ? streamOptions.initialCount
203
- : Infinity;
208
+ // PERF: we've already calculated this once; can we reference that again here?
209
+ const stream = evaluateStream(bucket, finishedStep);
210
+ const initialCount = stream?.initialCount ?? Infinity;
204
211
  const iterator = valueIsAsyncIterable
205
212
  ? value[Symbol.asyncIterator]()
206
213
  : value[Symbol.iterator]();
@@ -216,7 +223,7 @@ function executeBucket(bucket, requestContext) {
216
223
  // Optimization - defer everything
217
224
  const arr = [];
218
225
  arr[interfaces_js_1.$$streamMore] = iterator;
219
- finalResult[resultIndex] = arr;
226
+ bucket.setResult(finishedStep, resultIndex, arr, flags);
220
227
  }
221
228
  else {
222
229
  // Evaluate the first initialCount entries, rest is streamed.
@@ -232,11 +239,11 @@ function executeBucket(bucket, requestContext) {
232
239
  */
233
240
  let resultPromise;
234
241
  while ((resultPromise = iterator.next())) {
235
- const finalResult = await resultPromise;
236
- if (finalResult.done) {
242
+ const resolvedResult = await resultPromise;
243
+ if (resolvedResult.done) {
237
244
  break;
238
245
  }
239
- arr.push(await finalResult.value);
246
+ arr.push(await resolvedResult.value);
240
247
  if (++valuesSeen >= initialCount) {
241
248
  // This is safe to do in the `while` since we checked
242
249
  // the `0` entries condition in the optimization
@@ -245,11 +252,10 @@ function executeBucket(bucket, requestContext) {
245
252
  break;
246
253
  }
247
254
  }
248
- finalResult[resultIndex] = arr;
255
+ bucket.setResult(finishedStep, resultIndex, arr, flags);
249
256
  }
250
257
  catch (e) {
251
- bucket.hasErrors = true;
252
- finalResult[resultIndex] = (0, error_js_1.newGrafastError)(e, finishedStep.id);
258
+ bucket.setResult(finishedStep, resultIndex, e, flags | interfaces_js_1.FLAG_ERROR);
253
259
  }
254
260
  })();
255
261
  if (!promises) {
@@ -260,27 +266,25 @@ function executeBucket(bucket, requestContext) {
260
266
  }
261
267
  }
262
268
  }
263
- else if ((proto = Object.getPrototypeOf(value)) === null ||
264
- proto === Object.prototype) {
265
- finalResult[resultIndex] = value;
266
- }
267
- else if (value instanceof Error) {
268
- const e = error_js_1.$$error in value ? value : (0, error_js_1.newGrafastError)(value, finishedStep.id);
269
- finalResult[resultIndex] = e;
270
- bucket.hasErrors = true;
271
- }
272
269
  else {
273
- finalResult[resultIndex] = value;
270
+ bucket.setResult(finishedStep, resultIndex, value, flags);
274
271
  }
275
272
  };
276
273
  for (const allStepsIndex of indexesToProcess) {
277
274
  const step = _allSteps[allStepsIndex];
278
- const result = results[allStepsIndex];
279
- const storeEntry = bucket.store.get(step.id);
280
- for (let dataIndex = 0; dataIndex < size; dataIndex++) {
281
- const val = result[dataIndex];
275
+ const internalResult = resultList[allStepsIndex];
276
+ if (!internalResult) {
277
+ throw new Error(`GrafastInternalError<b82038d6-ca4e-4b55-8ed4-4d7c879f5dc2>: Result from ${step} should exist`);
278
+ }
279
+ const { flags, results } = internalResult;
280
+ const count = step._isUnary ? 1 : size;
281
+ for (let dataIndex = 0; dataIndex < count; dataIndex++) {
282
+ const val = results[dataIndex];
282
283
  if (step.isSyncAndSafe || !(0, utils_js_1.isPromiseLike)(val)) {
283
- success(step, storeEntry, dataIndex, val);
284
+ if (flags[dataIndex] == null) {
285
+ throw new Error(`GraphileInternalError<75df71bb-0f76-4a98-9664-9167d502296a>: result for ${step} has no flag at index ${dataIndex} (value = ${(0, inspect_js_1.inspect)(val)})`);
286
+ }
287
+ success(step, bucket, dataIndex, val, flags[dataIndex]);
284
288
  }
285
289
  else {
286
290
  if (!pendingPromises) {
@@ -301,41 +305,34 @@ function executeBucket(bucket, requestContext) {
301
305
  const settledResult = resultSettledResult[i];
302
306
  const { s: allStepsIndex, i: dataIndex } = pendingPromiseIndexes[i];
303
307
  const finishedStep = _allSteps[allStepsIndex];
304
- const storeEntry = bucket.store.get(finishedStep.id);
305
308
  if (settledResult.status === "fulfilled") {
306
- success(finishedStep, storeEntry, dataIndex, settledResult.value);
309
+ success(finishedStep, bucket, dataIndex, settledResult.value, interfaces_js_1.NO_FLAGS);
307
310
  }
308
311
  else {
309
- bucket.hasErrors = true;
310
- storeEntry[dataIndex] = (0, error_js_1.newGrafastError)(settledResult.reason, finishedStep.id);
312
+ const error = settledResult.reason;
313
+ bucket.setResult(finishedStep, dataIndex, error, interfaces_js_1.FLAG_ERROR);
311
314
  }
312
315
  }
313
- if (bucket.hasErrors && sideEffectSteps) {
314
- handleSideEffectSteps();
315
- }
316
316
  return promises ? Promise.all(promises) : undefined;
317
317
  })
318
318
  .then(null, (e) => {
319
319
  // THIS SHOULD NEVER HAPPEN!
320
320
  console.error(`GrafastInternalError<1e9731b4-005e-4b0e-bc61-43baa62e6444>: this error should never occur! Please file an issue against grafast. Details: ${e}`);
321
- bucket.hasErrors = true;
321
+ bucket.flagUnion |= interfaces_js_1.FLAG_ERROR;
322
322
  for (let i = 0, pendingPromisesLength = pendingPromises.length; i < pendingPromisesLength; i++) {
323
323
  const { s: allStepsIndex, i: dataIndex } = pendingPromiseIndexes[i];
324
324
  const finishedStep = _allSteps[allStepsIndex];
325
- const storeEntry = bucket.store.get(finishedStep.id);
326
- storeEntry[dataIndex] = (0, error_js_1.newGrafastError)(new Error(`GrafastInternalError<1e9731b4-005e-4b0e-bc61-43baa62e6444>: error occurred whilst performing completedStep(${finishedStep.id})`), finishedStep.id);
325
+ const error = new Error(`GrafastInternalError<1e9731b4-005e-4b0e-bc61-43baa62e6444>: error occurred whilst performing completedStep(${finishedStep.id})`);
326
+ bucket.setResult(finishedStep, dataIndex, error, interfaces_js_1.FLAG_ERROR);
327
327
  }
328
328
  });
329
329
  }
330
330
  else {
331
- if (bucket.hasErrors && sideEffectSteps) {
332
- handleSideEffectSteps();
333
- }
334
331
  return promises ? Promise.all(promises) : undefined;
335
332
  }
336
333
  };
337
334
  const runSyncSteps = () => {
338
- const executedLength = results.length;
335
+ const executedLength = resultList.length;
339
336
  if (dev_js_1.isDev) {
340
337
  assert.strictEqual(executedLength, phase.normalSteps?.length ?? 0, "Expected only and all normalSteps to have executed");
341
338
  }
@@ -351,48 +348,151 @@ function executeBucket(bucket, requestContext) {
351
348
  stopTime,
352
349
  meta,
353
350
  eventEmitter,
351
+ stream: evaluateStream(bucket, step),
354
352
  _bucket: bucket,
355
353
  _requestContext: requestContext,
356
354
  };
357
- bucket.store.set(step.id, (0, utils_js_1.arrayOfLength)(size));
358
- }
359
- outerLoop: for (let dataIndex = 0; dataIndex < size; dataIndex++) {
360
- if (sideEffectStepsWithErrors) {
361
- const currentPolymorphicPath = bucket.polymorphicPathList[dataIndex];
362
- for (const dep of sideEffectStepsWithErrors[currentPolymorphicPath ?? NO_POLY_PATH]) {
363
- const depVal = bucket.store.get(dep.id)[dataIndex];
364
- if (depVal === POLY_SKIPPED ||
365
- (dev_js_1.isDev && depVal?.$$error === POLY_SKIPPED)) {
366
- /* noop */
367
- }
368
- else if ((0, error_js_1.isGrafastError)(depVal)) {
369
- for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
370
- const step = _allSteps[allStepsIndex];
371
- const storeEntry = bucket.store.get(step.id);
372
- storeEntry[dataIndex] = depVal;
355
+ if (step._isUnary) {
356
+ // Handled later
357
+ }
358
+ else {
359
+ bucket.store.set(step.id, batchExecutionValue((0, utils_js_1.arrayOfLength)(size)));
360
+ }
361
+ }
362
+ for (let dataIndex = 0; dataIndex < size; dataIndex++) {
363
+ stepLoop: for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
364
+ const step = (0, utils_js_1.sudo)(_allSteps[allStepsIndex]);
365
+ // Unary steps only need to be processed once
366
+ if (step._isUnary && dataIndex !== 0) {
367
+ continue;
368
+ }
369
+ // Check if the side effect errored
370
+ const $sideEffect = step.implicitSideEffectStep;
371
+ if ($sideEffect) {
372
+ let currentPolymorphicPath;
373
+ if ($sideEffect.polymorphicPaths === null ||
374
+ (currentPolymorphicPath =
375
+ bucket.polymorphicPathList[dataIndex]) === null ||
376
+ $sideEffect.polymorphicPaths.has(currentPolymorphicPath)) {
377
+ const depExecutionValue = bucket.store.get($sideEffect.id);
378
+ if (depExecutionValue === undefined) {
379
+ throw new Error(`GrafastInternalError<fcc8d302-ac66-40e8-aaea-ee2c7e2b30b2>: failed to get result for side effect ${$sideEffect} which impacts ${step}`);
380
+ }
381
+ const depFlags = depExecutionValue._flagsAt(dataIndex);
382
+ if (depFlags & interfaces_js_1.FLAG_POLY_SKIPPED) {
383
+ /* noop */
384
+ }
385
+ else if (depFlags & interfaces_js_1.FLAG_ERROR) {
386
+ if (step._isUnary) {
387
+ // COPY the unary value
388
+ bucket.store.set(step.id, depExecutionValue);
389
+ bucket.flagUnion |= depFlags;
390
+ }
391
+ else {
392
+ const depVal = depExecutionValue.at(dataIndex);
393
+ bucket.setResult(step, dataIndex, depVal, depFlags);
394
+ }
395
+ continue stepLoop;
373
396
  }
374
- continue outerLoop;
375
397
  }
376
398
  }
377
- }
378
- stepLoop: for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
379
- const step = _allSteps[allStepsIndex];
380
- const storeEntry = bucket.store.get(step.id);
381
399
  try {
382
400
  const deps = [];
383
- for (const $dep of step.dependencies) {
384
- const depVal = bucket.store.get($dep.id)[dataIndex];
385
- if (bucket.hasErrors && (0, error_js_1.isGrafastError)(depVal)) {
386
- storeEntry[dataIndex] = depVal;
387
- continue stepLoop;
401
+ const extra = extras[allStepsIndex];
402
+ let forceIndexValue = undefined;
403
+ let rejectValue = undefined;
404
+ let indexFlags = interfaces_js_1.NO_FLAGS;
405
+ for (let i = 0, l = step.dependencies.length; i < l; i++) {
406
+ const $dep = step.dependencies[i];
407
+ const forbiddenFlags = step.dependencyForbiddenFlags[i];
408
+ const onReject = step.dependencyOnReject[i];
409
+ const depExecutionVal = bucket.store.get($dep.id);
410
+ // Search for "f2b3b1b3" for similar block
411
+ const flags = depExecutionVal._flagsAt(dataIndex);
412
+ const disallowedFlags = flags & forbiddenFlags;
413
+ if (disallowedFlags) {
414
+ indexFlags |= disallowedFlags;
415
+ // If there's a reject behavior and we're FRESHLY rejected (weren't
416
+ // already inhibited), use that as a fallback.
417
+ // TODO: validate this.
418
+ // If dep is inhibited and we don't allow inhibited, copy through (null or error).
419
+ // If dep is inhibited and we do allow inhibited, but we're disallowed, use our onReject.
420
+ // If dep is not inhibited, but we're disallowed, use our onReject.
421
+ if (onReject !== undefined &&
422
+ (disallowedFlags & (interfaces_js_1.FLAG_INHIBITED | interfaces_js_1.FLAG_ERROR)) === interfaces_js_1.NO_FLAGS) {
423
+ rejectValue ||= onReject;
424
+ }
425
+ if (forceIndexValue == null) {
426
+ if ((flags & interfaces_js_1.FLAG_ERROR) !== 0) {
427
+ const v = depExecutionVal.at(dataIndex);
428
+ forceIndexValue = v;
429
+ }
430
+ else {
431
+ forceIndexValue = null;
432
+ }
433
+ }
434
+ else {
435
+ // First error wins, ignore this second error.
436
+ }
437
+ // End "f2b3b1b3" block
438
+ }
439
+ else if (forceIndexValue === undefined) {
440
+ const depVal = depExecutionVal.at(dataIndex);
441
+ let depFlags;
442
+ if ((bucket.flagUnion & interfaces_js_1.FLAG_ERROR) === interfaces_js_1.FLAG_ERROR &&
443
+ ((depFlags = depExecutionVal._flagsAt(dataIndex)) &
444
+ interfaces_js_1.FLAG_ERROR) ===
445
+ interfaces_js_1.FLAG_ERROR) {
446
+ if (step._isUnary) {
447
+ // COPY the unary value
448
+ bucket.store.set(step.id, depExecutionVal);
449
+ bucket.flagUnion |= depFlags;
450
+ }
451
+ else {
452
+ bucket.setResult(step, dataIndex, depVal, interfaces_js_1.FLAG_ERROR);
453
+ }
454
+ continue stepLoop;
455
+ }
456
+ deps.push(depVal);
388
457
  }
389
- deps.push(depVal);
390
458
  }
391
- storeEntry[dataIndex] = step.unbatchedExecute(extras[allStepsIndex], ...deps);
459
+ let stepResult, stepFlags;
460
+ if (forceIndexValue !== undefined) {
461
+ // Search for "17217999b7a7" for similar block
462
+ if (forceIndexValue == null && rejectValue != null) {
463
+ indexFlags |= interfaces_js_1.FLAG_ERROR;
464
+ forceIndexValue = rejectValue;
465
+ }
466
+ else {
467
+ indexFlags |= interfaces_js_1.FLAG_INHIBITED;
468
+ }
469
+ // End "17217999b7a7" block
470
+ stepResult = forceIndexValue;
471
+ stepFlags = indexFlags;
472
+ }
473
+ else {
474
+ const rawStepResult = step.unbatchedExecute(extra, ...deps);
475
+ if (typeof rawStepResult === "object" &&
476
+ rawStepResult !== null &&
477
+ (0, error_js_1.isFlaggedValue)(rawStepResult)) {
478
+ stepResult = rawStepResult.value;
479
+ stepFlags = rawStepResult.flags;
480
+ }
481
+ else {
482
+ stepResult = rawStepResult;
483
+ stepFlags = rawStepResult == null ? interfaces_js_1.FLAG_NULL : interfaces_js_1.NO_FLAGS;
484
+ }
485
+ }
486
+ // TODO: what if stepResult is _returned_ error (as opposed to
487
+ // thrown)?
488
+ // NOTE: we are in `runSyncSteps` so this step is guaranteed to
489
+ // be "isSyncAndSafe". As such, we don't need to worry about it
490
+ // returning an error (unsafe) or a promise (async), we only
491
+ // need to check if it's null.
492
+ bucket.setResult(step, dataIndex, stepResult, stepFlags);
392
493
  }
393
494
  catch (e) {
394
- bucket.hasErrors = true;
395
- storeEntry[dataIndex] = (0, error_js_1.newGrafastError)(e, step.id);
495
+ bucket.setResult(step, dataIndex, e, interfaces_js_1.FLAG_ERROR);
396
496
  }
397
497
  }
398
498
  }
@@ -411,7 +511,7 @@ function executeBucket(bucket, requestContext) {
411
511
  const executePromise = executePromises[i];
412
512
  const index = executePromiseResultIndex[i];
413
513
  processedPromises.push(executePromise.then((promiseResult) => {
414
- results[index] = promiseResult;
514
+ resultList[index] = promiseResult;
415
515
  indexesPendingLoopOver.push(index);
416
516
  // We must loop over the results in the same tick in which the
417
517
  // promise resolved.
@@ -438,12 +538,26 @@ function executeBucket(bucket, requestContext) {
438
538
  return executeSamePhaseChildren();
439
539
  }
440
540
  // Function definitions below here
441
- function executeOrStream(step, dependencies, extra) {
442
- if (step._stepOptions.stream && (0, step_js_1.isStreamableStep)(step)) {
443
- return step.stream(size, dependencies, extra, step._stepOptions.stream);
541
+ function executeOrStream(count, step, values, extra) {
542
+ if (dev_js_1.isDev && step._isUnary && count !== 1) {
543
+ throw new Error(`GrafastInternalError<84a6cdfa-e8fe-4dea-85fe-9426a6a78027>: ${step} is a unary step, but we're attempting to pass it ${count} (!= 1) values`);
544
+ }
545
+ if (step.execute.length > 1) {
546
+ throw new Error(`${step} is using a legacy form of 'execute' which accepts multiple arguments, please see https://err.red/gev2`);
547
+ }
548
+ const executeDetails = {
549
+ indexMap: makeIndexMap(count),
550
+ indexForEach: makeIndexForEach(count),
551
+ count,
552
+ values,
553
+ extra,
554
+ stream: evaluateStream(bucket, step),
555
+ };
556
+ if (!step.isSyncAndSafe && middleware != null) {
557
+ return middleware.run("executeStep", { args, step, executeDetails }, executeStepFromEvent);
444
558
  }
445
559
  else {
446
- return step.execute(size, dependencies, extra);
560
+ return step.execute(executeDetails);
447
561
  }
448
562
  }
449
563
  // Slow mode...
@@ -452,107 +566,200 @@ function executeBucket(bucket, requestContext) {
452
566
  * polymorphicPaths from the input dependencies and then padding the lists
453
567
  * back out at the end.
454
568
  */
455
- function reallyExecuteStepWithErrorsOrSelective(step, dependenciesIncludingSideEffects, polymorphicPathList, extra) {
456
- const errors = Object.create(null);
457
- /** If there's errors, we must manipulate the arrays being passed into the step execution */
458
- let foundErrors = false;
569
+ function reallyExecuteStepWithFiltering(step, dependenciesIncludingSideEffects, dependencyForbiddenFlags, dependencyOnReject, polymorphicPathList, extra) {
570
+ const expectedSize = step._isUnary ? 1 : size;
571
+ const forcedValues = {
572
+ flags: (0, utils_js_1.arrayOfLength)(expectedSize, undefined),
573
+ results: (0, utils_js_1.arrayOfLength)(expectedSize, undefined),
574
+ };
575
+ /**
576
+ * If there's errors/forbidden values, we must manipulate the arrays being
577
+ * passed into the step execution
578
+ */
579
+ let needsTransform = false;
459
580
  /** If all we see is errors, there's no need to execute! */
460
- let needsNoExecution = true;
461
- const stepPolymorphicPaths = step.polymorphicPaths;
462
- for (let index = 0, l = polymorphicPathList.length; index < l; index++) {
463
- const polymorphicPath = polymorphicPathList[index];
581
+ let newSize = 0;
582
+ let stepPolymorphicPaths = step.polymorphicPaths;
583
+ const legitDepsCount = (0, utils_js_1.sudo)(step).dependencies.length;
584
+ const dependenciesIncludingSideEffectsCount = dependenciesIncludingSideEffects.length;
585
+ let dependencies = dependenciesIncludingSideEffectsCount > legitDepsCount
586
+ ? dependenciesIncludingSideEffects.slice(0, legitDepsCount)
587
+ : dependenciesIncludingSideEffects;
588
+ // OPTIM: if unariesIncludingSideEffects.some(isGrafastError) then shortcut execution because everything fails
589
+ let hasPolyMatch = true;
590
+ if (step._isUnary && stepPolymorphicPaths !== null) {
591
+ // Check that at least one datapoint matches one of our paths
592
+ hasPolyMatch = false;
593
+ for (let dataIndex = 0; dataIndex < size; dataIndex++) {
594
+ if (stepPolymorphicPaths.has(polymorphicPathList[dataIndex])) {
595
+ hasPolyMatch = true;
596
+ break;
597
+ }
598
+ }
599
+ stepPolymorphicPaths = null;
600
+ }
601
+ for (let dataIndex = 0; dataIndex < expectedSize; dataIndex++) {
602
+ let forceIndexValue = hasPolyMatch
603
+ ? undefined
604
+ : null;
605
+ let rejectValue = undefined;
606
+ let indexFlags = hasPolyMatch
607
+ ? interfaces_js_1.NO_FLAGS
608
+ : interfaces_js_1.FLAG_POLY_SKIPPED;
464
609
  if (stepPolymorphicPaths !== null &&
465
- !stepPolymorphicPaths.has(polymorphicPath)) {
466
- foundErrors = true;
467
- const e = dev_js_1.isDev && DEBUG_POLYMORPHISM
468
- ? Object.assign((0, error_js_1.newGrafastError)(new Error(`GrafastInternalError<00d52055-06b0-4b25-abeb-311b800ea284>: step ${step.id} (polymorphicPaths ${[
469
- ...stepPolymorphicPaths,
470
- ]}) has no match for '${polymorphicPath}'`), step.id), { $$error: POLY_SKIPPED })
471
- : POLY_SKIPPED;
472
- errors[index] = e;
473
- }
474
- else if (extra._bucket.hasErrors) {
475
- let noError = true;
476
- for (const depList of dependenciesIncludingSideEffects) {
477
- const v = depList[index];
478
- if ((0, error_js_1.isGrafastError)(v)) {
479
- if (!errors[index]) {
480
- noError = false;
481
- foundErrors = true;
482
- errors[index] = v;
483
- break;
610
+ !stepPolymorphicPaths.has(polymorphicPathList[dataIndex])) {
611
+ indexFlags |= interfaces_js_1.FLAG_POLY_SKIPPED;
612
+ forceIndexValue = null;
613
+ }
614
+ else if (extra._bucket.flagUnion) {
615
+ for (let i = 0; i < dependenciesIncludingSideEffectsCount; i++) {
616
+ const depExecutionVal = dependenciesIncludingSideEffects[i];
617
+ const forbiddenFlags = dependencyForbiddenFlags[i];
618
+ const onReject = dependencyOnReject[i];
619
+ // Search for "f2b3b1b3" for similar block
620
+ const flags = depExecutionVal._flagsAt(dataIndex);
621
+ const disallowedFlags = flags & forbiddenFlags;
622
+ if (disallowedFlags) {
623
+ indexFlags |= disallowedFlags;
624
+ // If there's a reject behavior and we're FRESHLY rejected (weren't
625
+ // already inhibited), use that as a fallback.
626
+ // TODO: validate this.
627
+ // If dep is inhibited and we don't allow inhibited, copy through (null or error).
628
+ // If dep is inhibited and we do allow inhibited, but we're disallowed, use our onReject.
629
+ // If dep is not inhibited, but we're disallowed, use our onReject.
630
+ if (onReject !== undefined &&
631
+ (disallowedFlags & (interfaces_js_1.FLAG_INHIBITED | interfaces_js_1.FLAG_ERROR)) === interfaces_js_1.NO_FLAGS) {
632
+ rejectValue ||= onReject;
633
+ }
634
+ if (forceIndexValue == null) {
635
+ if ((flags & interfaces_js_1.FLAG_ERROR) !== 0) {
636
+ const v = depExecutionVal.at(dataIndex);
637
+ forceIndexValue = v;
638
+ }
639
+ else {
640
+ forceIndexValue = null;
641
+ }
642
+ }
643
+ else {
644
+ // First error wins, ignore this second error.
484
645
  }
646
+ // End "f2b3b1b3" block
647
+ break;
485
648
  }
486
649
  }
487
- if (noError) {
488
- needsNoExecution = false;
650
+ }
651
+ else {
652
+ // All good
653
+ }
654
+ if (forceIndexValue !== undefined) {
655
+ if (!needsTransform) {
656
+ needsTransform = true;
657
+ // Clone up until this point, make mutable, don't add self
658
+ dependencies = dependencies.map((ev) => ev.isBatch
659
+ ? // TODO: move this creation to happen once the full list is
660
+ // already built, ideally we shouldn't be mutating an execution
661
+ // value later.
662
+ batchExecutionValue(ev.entries.slice(0, dataIndex))
663
+ : ev);
664
+ }
665
+ // Search for "17217999b7a7" for similar block
666
+ if (forceIndexValue == null && rejectValue != null) {
667
+ indexFlags |= interfaces_js_1.FLAG_ERROR;
668
+ forceIndexValue = rejectValue;
669
+ }
670
+ else {
671
+ indexFlags |= interfaces_js_1.FLAG_INHIBITED;
489
672
  }
673
+ // End "17217999b7a7" block
674
+ forcedValues.flags[dataIndex] = indexFlags;
675
+ forcedValues.results[dataIndex] = forceIndexValue;
490
676
  }
491
677
  else {
492
- needsNoExecution = false;
678
+ newSize++;
679
+ if (needsTransform) {
680
+ // dependenciesWithoutErrors has limited content; add this non-error value
681
+ for (let depListIndex = 0; depListIndex < legitDepsCount; depListIndex++) {
682
+ const depList = dependencies[depListIndex];
683
+ if (depList.isBatch) {
684
+ const depVal = dependenciesIncludingSideEffects[depListIndex];
685
+ depList.entries.push(depVal.at(dataIndex));
686
+ depList._flags.push(depVal._flagsAt(dataIndex));
687
+ }
688
+ }
689
+ }
493
690
  }
494
691
  }
495
- // Trim the side-effect dependencies back out again
496
- const dependencies = sideEffectStepsWithErrors
497
- ? dependenciesIncludingSideEffects.slice(0, step.dependencies.length)
498
- : dependenciesIncludingSideEffects;
499
- if (needsNoExecution) {
692
+ if (newSize === 0) {
500
693
  // Everything is errors; we can skip execution
501
- return Object.values(errors);
694
+ return forcedValues;
502
695
  }
503
- else if (foundErrors) {
504
- const dependenciesWithoutErrors = dependencies.map((depList) => depList.filter((_, index) => !errors[index]));
505
- const resultWithoutErrors = executeOrStream(step, dependenciesWithoutErrors, extra);
696
+ else if (needsTransform) {
697
+ const resultWithoutErrors = executeOrStream(newSize, step, dependencies, extra);
506
698
  if ((0, utils_js_1.isPromiseLike)(resultWithoutErrors)) {
507
- return resultWithoutErrors.then((r) => mergeErrorsBackIn(r, errors, dependencies[0].length));
699
+ return resultWithoutErrors.then((r) => mergeErrorsBackIn(r, forcedValues, expectedSize));
508
700
  }
509
701
  else {
510
- return mergeErrorsBackIn(resultWithoutErrors, errors, dependencies[0].length);
702
+ return mergeErrorsBackIn(resultWithoutErrors, forcedValues, expectedSize);
511
703
  }
512
704
  }
513
705
  else {
514
- return reallyExecuteStepWithNoErrors(step, dependencies, extra);
706
+ if (dev_js_1.isDev) {
707
+ assert.ok(newSize === expectedSize, "GrafastInternalError<47fca4ab-069c-428f-8374-267479c7fd27>: Expected newSize to equal expectedSize");
708
+ }
709
+ return reallyExecuteStepWithoutFiltering(newSize, step, dependencies, extra);
515
710
  }
516
711
  }
517
712
  /**
518
713
  * This function MIGHT throw or reject, so be sure to handle that.
519
714
  */
520
715
  function executeStep(step) {
716
+ // DELIBERATE SHADOWING!
717
+ const size = step._isUnary ? 1 : bucket.size;
521
718
  try {
522
719
  const meta = step.metaKey !== undefined ? metaByMetaKey[step.metaKey] : undefined;
523
720
  const extra = {
524
721
  stopTime,
525
722
  meta,
526
723
  eventEmitter,
724
+ stream: evaluateStream(bucket, step),
527
725
  _bucket: bucket,
528
726
  _requestContext: requestContext,
529
727
  };
530
- const dependencies = [];
531
- const depCount = step.dependencies.length;
532
- if (depCount > 0 || sideEffectStepsWithErrors !== null) {
728
+ /** Only mutate this inside `addDependency` */
729
+ const _rawDependencies = [];
730
+ const _rawForbiddenFlags = [];
731
+ const _rawOnReject = [];
732
+ const dependencies = _rawDependencies;
733
+ let needsFiltering = false;
734
+ const defaultForbiddenFlags = (0, utils_js_1.sudo)(step).defaultForbiddenFlags;
735
+ const addDependency = ($dep, forbiddenFlags, onReject) => {
736
+ if (step._isUnary && !$dep._isUnary) {
737
+ throw new Error(`GrafastInternalError<58bc38e2-8722-4c19-ba38-fd01a020654b>: unary step ${step} cannot be made dependent on non-unary step ${$dep}!`);
738
+ }
739
+ const executionValue = store.get($dep.id);
740
+ if (executionValue === undefined) {
741
+ throw new Error(`GrafastInternalError<d9e9eb37-4251-4659-a545-4730826ecf0e>: ${$dep} data couldn't be found, but required by ${step} (with side effect ${step.implicitSideEffectStep})!`);
742
+ }
743
+ _rawDependencies.push(executionValue);
744
+ _rawForbiddenFlags.push(forbiddenFlags);
745
+ _rawOnReject.push(onReject);
746
+ if (!needsFiltering && (bucket.flagUnion & forbiddenFlags) !== 0) {
747
+ const flags = executionValue._getStateUnion();
748
+ needsFiltering = (flags & forbiddenFlags) !== 0;
749
+ }
750
+ };
751
+ const sstep = (0, utils_js_1.sudo)(step);
752
+ const depCount = sstep.dependencies.length;
753
+ if (depCount > 0) {
533
754
  for (let i = 0, l = depCount; i < l; i++) {
534
- const $dep = step.dependencies[i];
535
- dependencies[i] = store.get($dep.id);
536
- }
537
- if (sideEffectStepsWithErrors !== null) {
538
- if (step.polymorphicPaths) {
539
- for (const path of step.polymorphicPaths) {
540
- for (const sideEffectStep of sideEffectStepsWithErrors[path]) {
541
- const sideEffectStoreEntry = store.get(sideEffectStep.id);
542
- if (!dependencies.includes(sideEffectStoreEntry)) {
543
- dependencies.push(sideEffectStoreEntry);
544
- }
545
- }
546
- }
547
- }
548
- else {
549
- for (const sideEffectStep of sideEffectStepsWithErrors[NO_POLY_PATH]) {
550
- const sideEffectStoreEntry = store.get(sideEffectStep.id);
551
- if (!dependencies.includes(sideEffectStoreEntry)) {
552
- dependencies.push(sideEffectStoreEntry);
553
- }
554
- }
555
- }
755
+ const $dep = sstep.dependencies[i];
756
+ addDependency($dep, sstep.dependencyForbiddenFlags[i], sstep.dependencyOnReject[i]);
757
+ }
758
+ }
759
+ const $sideEffect = step.implicitSideEffectStep;
760
+ if ($sideEffect) {
761
+ if ($sideEffect._isUnary || !step._isUnary) {
762
+ addDependency($sideEffect, defaultForbiddenFlags, undefined);
556
763
  }
557
764
  }
558
765
  if (dev_js_1.isDev &&
@@ -560,16 +767,24 @@ function executeBucket(bucket, requestContext) {
560
767
  step.polymorphicPaths === null) {
561
768
  throw new Error(`GrafastInternalError<c33328fe-6758-4699-8ac6-7be41ce58bfb>: a step without polymorphic paths cannot belong to a polymorphic bucket`);
562
769
  }
563
- const isSelectiveStep = step.layerPlan.reason.type === "polymorphic" &&
564
- step.polymorphicPaths.size !==
565
- step.layerPlan.reason.polymorphicPaths.size;
566
- const result = bucket.hasErrors || isSelectiveStep
567
- ? reallyExecuteStepWithErrorsOrSelective(step, dependencies, bucket.polymorphicPathList, extra)
568
- : reallyExecuteStepWithNoErrors(step, dependencies, extra);
770
+ if (!needsFiltering) {
771
+ const isSelectiveStep = step.layerPlan.reason.type === "polymorphic" &&
772
+ step.polymorphicPaths.size !==
773
+ step.layerPlan.reason.polymorphicPaths.size;
774
+ needsFiltering ||= isSelectiveStep;
775
+ }
776
+ const result = needsFiltering
777
+ ? reallyExecuteStepWithFiltering(step, dependencies, _rawForbiddenFlags, _rawOnReject, bucket.polymorphicPathList, extra)
778
+ : reallyExecuteStepWithoutFiltering(size, step, $sideEffect ? dependencies.slice(0, depCount) : dependencies, extra);
569
779
  if ((0, utils_js_1.isPromiseLike)(result)) {
570
780
  return result.then(null, (error) => {
571
- // bucket.hasErrors = true;
572
- return (0, utils_js_1.arrayOfLength)(size, error);
781
+ // Don't need to do this here, it will be done where the
782
+ // ExecutionValue is created:
783
+ // bucket.hasNonZeroStatus = true;
784
+ return {
785
+ flags: (0, utils_js_1.arrayOfLength)(size, interfaces_js_1.FLAG_ERROR | interfaces_js_1.FLAG_STOPPED),
786
+ results: (0, utils_js_1.arrayOfLength)(size, error),
787
+ };
573
788
  });
574
789
  }
575
790
  else {
@@ -577,8 +792,13 @@ function executeBucket(bucket, requestContext) {
577
792
  }
578
793
  }
579
794
  catch (error) {
580
- // bucket.hasErrors = true;
581
- return (0, utils_js_1.arrayOfLength)(size, error);
795
+ // Don't need to do this here, it will be done where the
796
+ // ExecutionValue is created:
797
+ // bucket.hasNonZeroStatus = true;
798
+ return {
799
+ flags: (0, utils_js_1.arrayOfLength)(size, interfaces_js_1.FLAG_ERROR | interfaces_js_1.FLAG_STOPPED),
800
+ results: (0, utils_js_1.arrayOfLength)(size, error),
801
+ };
582
802
  }
583
803
  }
584
804
  function executeSamePhaseChildren() {
@@ -591,8 +811,10 @@ function executeBucket(bucket, requestContext) {
591
811
  * are called.
592
812
  */
593
813
  const enqueue = (callback) => {
594
- const result = (mutationQueue ?? Promise.resolve()).then(callback);
595
- mutationQueue = result.then(noop, noop);
814
+ const result = mutationQueue ? mutationQueue.then(callback) : callback();
815
+ if ((0, utils_js_1.isPromiseLike)(result)) {
816
+ mutationQueue = result.then(noop, noop);
817
+ }
596
818
  return result;
597
819
  };
598
820
  loop: for (const childLayerPlan of childLayerPlans) {
@@ -615,7 +837,9 @@ function executeBucket(bucket, requestContext) {
615
837
  if (childBucket !== null) {
616
838
  // Enqueue for execution (mutations must run in order)
617
839
  const promise = enqueue(() => executeBucket(childBucket, requestContext));
618
- childPromises.push(promise);
840
+ if ((0, utils_js_1.isPromiseLike)(promise)) {
841
+ childPromises.push(promise);
842
+ }
619
843
  }
620
844
  break;
621
845
  }
@@ -648,7 +872,6 @@ function executeBucket(bucket, requestContext) {
648
872
  }
649
873
  }
650
874
  }
651
- exports.executeBucket = executeBucket;
652
875
  /** @internal */
653
876
  function newBucket(spec, parentMetaByMetaKey) {
654
877
  if (dev_js_1.isDev) {
@@ -668,8 +891,10 @@ function newBucket(spec, parentMetaByMetaKey) {
668
891
  }
669
892
  */
670
893
  for (const [key, list] of spec.store.entries()) {
671
- assert.ok(Array.isArray(list), `Store entry for step '${key}' for layerPlan '${spec.layerPlan.id}' should be a list`);
672
- assert.strictEqual(list.length, spec.size, `Store entry for step '${key}' for layerPlan '${spec.layerPlan.id}' should have same length as bucket`);
894
+ assert.ok(typeof list.isBatch === "boolean", `Store entry for step '${key}' for layerPlan '${spec.layerPlan.id}' should be an ExecutionValue`);
895
+ if (list.isBatch) {
896
+ assert.strictEqual(list.entries.length, spec.size, `Store entry for step '${key}' for layerPlan '${spec.layerPlan.id}' should have same length as bucket`);
897
+ }
673
898
  }
674
899
  if (!spec.iterators) {
675
900
  throw new Error(`newBucket called but no iterators array was specified`);
@@ -683,18 +908,195 @@ function newBucket(spec, parentMetaByMetaKey) {
683
908
  ? // Reset the metaByMetaKey
684
909
  spec.layerPlan.operationPlan.makeMetaByMetaKey()
685
910
  : parentMetaByMetaKey;
911
+ // Copy from spec
912
+ const { layerPlan, store, size, flagUnion, polymorphicPathList, iterators } = spec;
913
+ const children = Object.create(null);
686
914
  return {
687
- // Copy from spec
688
- layerPlan: spec.layerPlan,
689
- store: spec.store,
690
- size: spec.size,
691
- hasErrors: spec.hasErrors,
692
- polymorphicPathList: spec.polymorphicPathList,
693
- iterators: spec.iterators,
915
+ toString: bucketToString,
916
+ layerPlan,
917
+ store,
918
+ size,
919
+ flagUnion,
920
+ polymorphicPathList,
921
+ iterators,
694
922
  metaByMetaKey,
695
923
  isComplete: false,
696
- children: Object.create(null),
924
+ children,
925
+ setResult(step, index, value, flags) {
926
+ const stepId = step.id;
927
+ if (index >= size) {
928
+ throw new Error(`GrafastInternalError<9465db89-cc9d-415c-97e7-57bb19ddebe0>: attempt to write to out of bounds index ${index} for bucket of size ${size} for step ${step}`);
929
+ }
930
+ if (flags !== 0) {
931
+ this.flagUnion |= flags;
932
+ }
933
+ if (step._isUnary) {
934
+ if (dev_js_1.isDev && store.has(stepId)) {
935
+ const ev = store.get(stepId);
936
+ console.error(`GrafastInternalWarning<603e635f-af46-4feb-abb1-bac184bf7ef2>: value for step ${step} has already been stored to bucket ${layerPlan}; overwriting.`, {
937
+ isBatch: ev?.isBatch,
938
+ value0: ev?.at(0),
939
+ flags0: ev?._flagsAt(0),
940
+ }, { isBatch: false, value, flags });
941
+ }
942
+ const ev = unaryExecutionValue(value, flags);
943
+ store.set(stepId, ev);
944
+ }
945
+ else {
946
+ const storeEntry = store.get(stepId);
947
+ if (!storeEntry || !storeEntry.isBatch) {
948
+ if (!storeEntry) {
949
+ throw new Error(`GrafastInternalError<e2ba1e4a-5391-4f81-bb21-fb200e29aea2>: value for step ${step} expected to be a batch value, but is not set`);
950
+ }
951
+ else {
952
+ throw new Error(`GrafastInternalError<f587d317-8919-4f14-905a-ab7bb338dd2b>: value for step ${step} expected to be a batch value, but is unary (non-batch)`);
953
+ }
954
+ }
955
+ storeEntry._setResult(index, value, flags);
956
+ }
957
+ },
697
958
  };
698
959
  }
699
- exports.newBucket = newBucket;
960
+ function bucketToString() {
961
+ return `Bucket<${this.layerPlan}>`;
962
+ }
963
+ // NOTE: I evaluated using `__proto__: batchExecutionValueProto` to extract the
964
+ // shared properties of these objects to see if performance was improved, but
965
+ // this was actually a net loss in performance.
966
+ //
967
+ // This is also evidence that you shouldn't trust ChatGPT for performance
968
+ // advice, and should always run your own benchmarks instead:
969
+ // https://chatgpt.com/share/67d1746f-4da8-8012-bdf8-707e54a4238e
970
+ // TODO: memoize?
971
+ function batchExecutionValue(entries, _flags = (0, utils_js_1.arrayOfLength)(entries.length, 0)) {
972
+ return {
973
+ // Try and keep these properties in the same order as unaryExecutionValue
974
+ isBatch: true,
975
+ at: batchEntriesAt,
976
+ unaryValue: batchThrowNotUnary,
977
+ _flagsAt: batchFlagsAt,
978
+ _getStateUnion: batchGetStateUnion,
979
+ _setResult: batchSetResult,
980
+ _copyResult: batchCopyResult,
981
+ entries,
982
+ _flags,
983
+ _cachedStateUnion: null,
984
+ };
985
+ }
986
+ // TODO: memoize?
987
+ function unaryExecutionValue(value, _entryFlags = 0) {
988
+ return {
989
+ // Try and keep these properties in the same order as batchExecutionValue
990
+ isBatch: false,
991
+ at: unaryAt,
992
+ unaryValue: unaryThisDotValue,
993
+ _flagsAt: unaryFlagsAt,
994
+ _getStateUnion: unaryGetStateUnion,
995
+ _setResult: unarySetResult,
996
+ _copyResult: unaryCopyResult,
997
+ value,
998
+ _entryFlags,
999
+ };
1000
+ }
1001
+ function batchThrowNotUnary() {
1002
+ throw new Error(`This is not a unary value so we cannot get the single value - there may be more than one!`);
1003
+ }
1004
+ function batchGetStateUnion() {
1005
+ if (this._cachedStateUnion === null) {
1006
+ let u = interfaces_js_1.NO_FLAGS;
1007
+ for (const flag of this._flags) {
1008
+ u = u | flag;
1009
+ }
1010
+ this._cachedStateUnion = u;
1011
+ }
1012
+ return this._cachedStateUnion;
1013
+ }
1014
+ function batchEntriesAt(i) {
1015
+ return this.entries[i];
1016
+ }
1017
+ function batchFlagsAt(i) {
1018
+ return this._flags[i];
1019
+ }
1020
+ function batchSetResult(i, value, flags) {
1021
+ this.entries[i] = value;
1022
+ this._flags[i] = flags;
1023
+ }
1024
+ // NOTE: batchCopyResult and unaryCopyResult are **identical**, but we don't
1025
+ // want a single megamorphic function so we define it twice.
1026
+ function batchCopyResult(targetIndex, source, sourceIndex) {
1027
+ this._setResult(targetIndex, source.at(sourceIndex), source._flagsAt(sourceIndex));
1028
+ }
1029
+ function unaryCopyResult(targetIndex, source, sourceIndex) {
1030
+ this._setResult(targetIndex, source.at(sourceIndex), source._flagsAt(sourceIndex));
1031
+ }
1032
+ function unaryThisDotValue() {
1033
+ return this.value;
1034
+ }
1035
+ function unaryAt() {
1036
+ return this.value;
1037
+ }
1038
+ function unaryFlagsAt() {
1039
+ return this._entryFlags;
1040
+ }
1041
+ function unaryGetStateUnion() {
1042
+ return this._entryFlags;
1043
+ }
1044
+ function unarySetResult(i, value, flags) {
1045
+ if (i !== 0) {
1046
+ throw new Error(`Unary step only expects one result`);
1047
+ }
1048
+ this.value = value;
1049
+ this._entryFlags = flags;
1050
+ }
1051
+ const indexMapCache = new Map();
1052
+ function makeIndexMap(count) {
1053
+ const existing = indexMapCache.get(count);
1054
+ if (existing !== undefined) {
1055
+ return existing;
1056
+ }
1057
+ const result = (callback) => {
1058
+ const results = [];
1059
+ for (let i = 0; i < count; i++) {
1060
+ results.push(callback(i));
1061
+ }
1062
+ return results;
1063
+ };
1064
+ if (count <= 100) {
1065
+ indexMapCache.set(count, result);
1066
+ }
1067
+ return result;
1068
+ }
1069
+ const indexForEachCache = new Map();
1070
+ function makeIndexForEach(count) {
1071
+ const existing = indexForEachCache.get(count);
1072
+ if (existing !== undefined) {
1073
+ return existing;
1074
+ }
1075
+ const result = (callback) => {
1076
+ for (let i = 0; i < count; i++) {
1077
+ callback(i);
1078
+ }
1079
+ };
1080
+ if (count <= 100) {
1081
+ indexForEachCache.set(count, result);
1082
+ }
1083
+ return result;
1084
+ }
1085
+ function executeStepFromEvent(event) {
1086
+ return event.step.execute(event.executeDetails);
1087
+ }
1088
+ function evaluateStream(bucket, step) {
1089
+ const stream = step._stepOptions.stream;
1090
+ if (!stream)
1091
+ return null;
1092
+ const shouldStream = stream.ifStepId == null
1093
+ ? true
1094
+ : (bucket.store.get(stream.ifStepId)?.unaryValue() ?? true);
1095
+ if (!shouldStream)
1096
+ return null;
1097
+ const initialCount = stream.initialCountStepId == null
1098
+ ? 0
1099
+ : (bucket.store.get(stream.initialCountStepId)?.unaryValue() ?? 0);
1100
+ return { initialCount };
1101
+ }
700
1102
  //# sourceMappingURL=executeBucket.js.map