grafast 0.1.1-beta.0 → 0.1.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/args.js +62 -0
- package/dist/assert.js +24 -0
- package/dist/bucket.js +4 -0
- package/dist/config.js +99 -0
- package/dist/constraints.js +72 -0
- package/dist/deferred.js +25 -0
- package/dist/dev.js +10 -0
- package/dist/engine/LayerPlan.js +536 -0
- package/dist/engine/OperationPlan.js +2543 -0
- package/dist/engine/OutputPlan.js +1059 -0
- package/dist/engine/StepTracker.js +524 -0
- package/dist/engine/executeBucket.js +700 -0
- package/dist/engine/executeOutputPlan.js +21 -0
- package/dist/engine/lib/withGlobalLayerPlan.js +36 -0
- package/dist/envelop.js +73 -5
- package/dist/error.js +62 -0
- package/dist/establishOperationPlan.js +186 -0
- package/dist/execute.js +80 -0
- package/dist/exportAs.js +57 -0
- package/dist/global.js +13 -0
- package/dist/grafastGraphql.js +133 -0
- package/dist/grafastPrint.js +177 -0
- package/dist/graphqlCollectFields.js +217 -0
- package/dist/graphqlMergeSelectionSets.js +60 -0
- package/dist/index.d.ts +161 -2
- package/dist/index.js +255 -499
- package/dist/input.js +159 -0
- package/dist/inspect.js +20 -0
- package/dist/interfaces.d.ts +3 -160
- package/dist/interfaces.js +63 -0
- package/dist/makeGrafastSchema.js +174 -0
- package/dist/mermaid.js +290 -7
- package/dist/operationPlan-input.js +449 -0
- package/dist/planJSONInterfaces.js +3 -0
- package/dist/polymorphic.js +73 -0
- package/dist/prepare.js +763 -0
- package/dist/step.js +396 -0
- package/dist/steps/__inputDynamicScalar.js +125 -0
- package/dist/steps/__inputList.js +110 -0
- package/dist/steps/__inputObject.js +128 -0
- package/dist/steps/__inputStaticLeaf.js +43 -0
- package/dist/steps/__item.js +50 -0
- package/dist/steps/__trackedValue.js +257 -0
- package/dist/steps/__value.js +47 -0
- package/dist/steps/access.js +183 -0
- package/dist/steps/applyTransforms.js +151 -0
- package/dist/steps/connection.js +315 -0
- package/dist/steps/constant.js +72 -0
- package/dist/steps/each.js +64 -0
- package/dist/steps/error.js +32 -0
- package/dist/steps/filter.js +32 -0
- package/dist/steps/first.js +49 -0
- package/dist/steps/graphqlResolver.js +260 -0
- package/dist/steps/groupBy.js +40 -0
- package/dist/steps/index.js +117 -0
- package/dist/steps/lambda.js +58 -0
- package/dist/steps/last.js +41 -0
- package/dist/steps/list.js +75 -0
- package/dist/steps/listTransform.js +185 -0
- package/dist/steps/listen.js +61 -0
- package/dist/steps/load.d.ts +4 -4
- package/dist/steps/load.js +310 -0
- package/dist/steps/node.d.ts +6 -6
- package/dist/steps/node.js +107 -0
- package/dist/steps/object.js +195 -0
- package/dist/steps/partitionByIndex.js +86 -0
- package/dist/steps/proxy.js +114 -0
- package/dist/steps/remapKeys.js +84 -0
- package/dist/steps/reverse.js +52 -0
- package/dist/steps/setter.js +28 -0
- package/dist/stringifyPayload.js +59 -0
- package/dist/stripAnsi.js +14 -0
- package/dist/subscribe.js +14 -0
- package/dist/thereCanBeOnlyOne.js +21 -0
- package/dist/timeSource.js +8 -0
- package/dist/utils.js +698 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +6 -0
- package/package.json +9 -4
- package/dist/LICENSES.txt +0 -300
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.newBucket = exports.executeBucket = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const iterall_1 = require("iterall");
|
|
6
|
+
const assert = tslib_1.__importStar(require("../assert.js"));
|
|
7
|
+
const dev_js_1 = require("../dev.js");
|
|
8
|
+
const error_js_1 = require("../error.js");
|
|
9
|
+
const inspect_js_1 = require("../inspect.js");
|
|
10
|
+
const interfaces_js_1 = require("../interfaces.js");
|
|
11
|
+
const step_js_1 = require("../step.js");
|
|
12
|
+
const timeSource_js_1 = require("../timeSource.js");
|
|
13
|
+
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
|
+
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
|
+
function noop() {
|
|
24
|
+
/*noop*/
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Takes a list of `results` (shorter than `resultCount`) and an object with
|
|
28
|
+
* errors and indexes; returns a list of length `resultCount` with the results
|
|
29
|
+
* from `results` but with errors injected at the indexes specified in
|
|
30
|
+
* `errors`.
|
|
31
|
+
*
|
|
32
|
+
* ASSERT: `results.length + Object.values(errors).length === resultCount`
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
function mergeErrorsBackIn(results, errors, resultCount) {
|
|
37
|
+
const finalResults = [];
|
|
38
|
+
let resultIndex = 0;
|
|
39
|
+
for (let i = 0; i < resultCount; i++) {
|
|
40
|
+
const error = errors[i];
|
|
41
|
+
if (error !== undefined) {
|
|
42
|
+
finalResults[i] = error;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
finalResults[i] = results[resultIndex++];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return finalResults;
|
|
49
|
+
}
|
|
50
|
+
/** @internal */
|
|
51
|
+
function executeBucket(bucket, requestContext) {
|
|
52
|
+
/**
|
|
53
|
+
* Execute the step directly; since there's no errors we can pass the
|
|
54
|
+
* dependencies through verbatim!
|
|
55
|
+
*/
|
|
56
|
+
const reallyExecuteStepWithNoErrors = executeOrStream;
|
|
57
|
+
const { stopTime, eventEmitter } = requestContext;
|
|
58
|
+
const { metaByMetaKey, size, store, layerPlan: { phases, children: childLayerPlans }, } = bucket;
|
|
59
|
+
const phaseCount = phases.length;
|
|
60
|
+
let sideEffectStepsWithErrors = null;
|
|
61
|
+
// Like a `for(i = 0; i < phaseCount; i++)` loop with some `await`s in it, except it does promise
|
|
62
|
+
// handling manually so that it can complete synchronously (no promises) if
|
|
63
|
+
// possible.
|
|
64
|
+
const nextPhase = (phaseIndex) => {
|
|
65
|
+
if (phaseIndex >= phaseCount) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const phase = phases[phaseIndex];
|
|
69
|
+
const { _allSteps } = phase;
|
|
70
|
+
/**
|
|
71
|
+
* To ensure we don't enter a situation where an "unhandled" promise
|
|
72
|
+
* rejection causes Node to exit, we must process each completed step during the
|
|
73
|
+
* same tick in which it completes.
|
|
74
|
+
*/
|
|
75
|
+
let indexesPendingLoopOver = [];
|
|
76
|
+
let executePromises = null;
|
|
77
|
+
let sideEffectSteps = null;
|
|
78
|
+
let executePromiseResultIndex = null;
|
|
79
|
+
const results = [];
|
|
80
|
+
if (phase.checkTimeout &&
|
|
81
|
+
stopTime !== null &&
|
|
82
|
+
timeSource_js_1.timeSource.now() >= stopTime) {
|
|
83
|
+
// ABORT!
|
|
84
|
+
if (phase.normalSteps !== undefined) {
|
|
85
|
+
const normalSteps = phase.normalSteps;
|
|
86
|
+
for (let normalStepIndex = 0, l = normalSteps.length; normalStepIndex < l; normalStepIndex++) {
|
|
87
|
+
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;
|
|
91
|
+
indexesPendingLoopOver.push(normalStepIndex);
|
|
92
|
+
bucket.hasErrors = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (phase.normalSteps !== undefined) {
|
|
97
|
+
const normalSteps = phase.normalSteps;
|
|
98
|
+
for (let normalStepIndex = 0, l = normalSteps.length; normalStepIndex < l; normalStepIndex++) {
|
|
99
|
+
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
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const r = executeStep(step);
|
|
110
|
+
if ((0, utils_js_1.isPromiseLike)(r)) {
|
|
111
|
+
results[normalStepIndex] = undefined /* will populate shortly */;
|
|
112
|
+
if (!executePromises) {
|
|
113
|
+
executePromises = [r];
|
|
114
|
+
executePromiseResultIndex = [normalStepIndex];
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const newIndex = executePromises.push(r) - 1;
|
|
118
|
+
executePromiseResultIndex[newIndex] = normalStepIndex;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
results[normalStepIndex] = r;
|
|
123
|
+
indexesPendingLoopOver.push(normalStepIndex);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
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;
|
|
130
|
+
indexesPendingLoopOver.push(normalStepIndex);
|
|
131
|
+
bucket.hasErrors = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
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
|
+
const next = () => {
|
|
154
|
+
return nextPhase(phaseIndex + 1);
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Loops over (and resets) the indexesPendingLoopOver array, ensuring that
|
|
158
|
+
* all errors are handled.
|
|
159
|
+
*/
|
|
160
|
+
const loopOverResults = () => {
|
|
161
|
+
if (indexesPendingLoopOver.length === 0)
|
|
162
|
+
return;
|
|
163
|
+
const indexesToProcess = indexesPendingLoopOver;
|
|
164
|
+
indexesPendingLoopOver = [];
|
|
165
|
+
// Validate executed steps
|
|
166
|
+
for (const allStepsIndex of indexesToProcess) {
|
|
167
|
+
const result = results[allStepsIndex];
|
|
168
|
+
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 })}`);
|
|
173
|
+
}
|
|
174
|
+
throw new Error(`Result array from ${finishedStep} should have length ${size}, instead it had length ${result.length}`);
|
|
175
|
+
}
|
|
176
|
+
bucket.store.set(finishedStep.id, (0, utils_js_1.arrayOfLength)(size));
|
|
177
|
+
}
|
|
178
|
+
// Need to complete promises, check for errors, etc.
|
|
179
|
+
// **DO NOT THROW, DO NOT ALLOW AN ERROR TO BE RAISED!**
|
|
180
|
+
// **USE DEFENSIVE PROGRAMMING HERE!**
|
|
181
|
+
/** PROMISES ADDED HERE MUST NOT REJECT */
|
|
182
|
+
let promises;
|
|
183
|
+
let pendingPromises;
|
|
184
|
+
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;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
let valueIsAsyncIterable;
|
|
195
|
+
if (
|
|
196
|
+
// Are we streaming this? If so, we need an iterable or async
|
|
197
|
+
// iterable.
|
|
198
|
+
finishedStep._stepOptions.stream &&
|
|
199
|
+
((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;
|
|
204
|
+
const iterator = valueIsAsyncIterable
|
|
205
|
+
? value[Symbol.asyncIterator]()
|
|
206
|
+
: value[Symbol.iterator]();
|
|
207
|
+
// Here we track the iterator via the bucket, this allows us to
|
|
208
|
+
// ensure that the iterator is terminated even if the stream is never
|
|
209
|
+
// consumed (e.g. if an error is thrown/caught during execution of
|
|
210
|
+
// the output plan).
|
|
211
|
+
if (!bucket.iterators[resultIndex]) {
|
|
212
|
+
bucket.iterators[resultIndex] = new Set();
|
|
213
|
+
}
|
|
214
|
+
bucket.iterators[resultIndex].add(iterator);
|
|
215
|
+
if (initialCount === 0) {
|
|
216
|
+
// Optimization - defer everything
|
|
217
|
+
const arr = [];
|
|
218
|
+
arr[interfaces_js_1.$$streamMore] = iterator;
|
|
219
|
+
finalResult[resultIndex] = arr;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// Evaluate the first initialCount entries, rest is streamed.
|
|
223
|
+
const promise = (async () => {
|
|
224
|
+
try {
|
|
225
|
+
let valuesSeen = 0;
|
|
226
|
+
const arr = [];
|
|
227
|
+
/*
|
|
228
|
+
* We need to "shift" a few entries off the top of the
|
|
229
|
+
* iterator, but still keep it iterable for the later
|
|
230
|
+
* stream. To accomplish this we have to do manual
|
|
231
|
+
* looping
|
|
232
|
+
*/
|
|
233
|
+
let resultPromise;
|
|
234
|
+
while ((resultPromise = iterator.next())) {
|
|
235
|
+
const finalResult = await resultPromise;
|
|
236
|
+
if (finalResult.done) {
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
arr.push(await finalResult.value);
|
|
240
|
+
if (++valuesSeen >= initialCount) {
|
|
241
|
+
// This is safe to do in the `while` since we checked
|
|
242
|
+
// the `0` entries condition in the optimization
|
|
243
|
+
// above.
|
|
244
|
+
arr[interfaces_js_1.$$streamMore] = iterator;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
finalResult[resultIndex] = arr;
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
bucket.hasErrors = true;
|
|
252
|
+
finalResult[resultIndex] = (0, error_js_1.newGrafastError)(e, finishedStep.id);
|
|
253
|
+
}
|
|
254
|
+
})();
|
|
255
|
+
if (!promises) {
|
|
256
|
+
promises = [promise];
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
promises.push(promise);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
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
|
+
else {
|
|
273
|
+
finalResult[resultIndex] = value;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
for (const allStepsIndex of indexesToProcess) {
|
|
277
|
+
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];
|
|
282
|
+
if (step.isSyncAndSafe || !(0, utils_js_1.isPromiseLike)(val)) {
|
|
283
|
+
success(step, storeEntry, dataIndex, val);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
if (!pendingPromises) {
|
|
287
|
+
pendingPromises = [val];
|
|
288
|
+
pendingPromiseIndexes = [{ s: allStepsIndex, i: dataIndex }];
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
pendingPromises.push(val);
|
|
292
|
+
pendingPromiseIndexes.push({ s: allStepsIndex, i: dataIndex });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (pendingPromises !== undefined) {
|
|
298
|
+
return Promise.allSettled(pendingPromises)
|
|
299
|
+
.then((resultSettledResult) => {
|
|
300
|
+
for (let i = 0, pendingPromisesLength = resultSettledResult.length; i < pendingPromisesLength; i++) {
|
|
301
|
+
const settledResult = resultSettledResult[i];
|
|
302
|
+
const { s: allStepsIndex, i: dataIndex } = pendingPromiseIndexes[i];
|
|
303
|
+
const finishedStep = _allSteps[allStepsIndex];
|
|
304
|
+
const storeEntry = bucket.store.get(finishedStep.id);
|
|
305
|
+
if (settledResult.status === "fulfilled") {
|
|
306
|
+
success(finishedStep, storeEntry, dataIndex, settledResult.value);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
bucket.hasErrors = true;
|
|
310
|
+
storeEntry[dataIndex] = (0, error_js_1.newGrafastError)(settledResult.reason, finishedStep.id);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (bucket.hasErrors && sideEffectSteps) {
|
|
314
|
+
handleSideEffectSteps();
|
|
315
|
+
}
|
|
316
|
+
return promises ? Promise.all(promises) : undefined;
|
|
317
|
+
})
|
|
318
|
+
.then(null, (e) => {
|
|
319
|
+
// THIS SHOULD NEVER HAPPEN!
|
|
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;
|
|
322
|
+
for (let i = 0, pendingPromisesLength = pendingPromises.length; i < pendingPromisesLength; i++) {
|
|
323
|
+
const { s: allStepsIndex, i: dataIndex } = pendingPromiseIndexes[i];
|
|
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);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
if (bucket.hasErrors && sideEffectSteps) {
|
|
332
|
+
handleSideEffectSteps();
|
|
333
|
+
}
|
|
334
|
+
return promises ? Promise.all(promises) : undefined;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
const runSyncSteps = () => {
|
|
338
|
+
const executedLength = results.length;
|
|
339
|
+
if (dev_js_1.isDev) {
|
|
340
|
+
assert.strictEqual(executedLength, phase.normalSteps?.length ?? 0, "Expected only and all normalSteps to have executed");
|
|
341
|
+
}
|
|
342
|
+
if (!phase.unbatchedSyncAndSafeSteps) {
|
|
343
|
+
return next();
|
|
344
|
+
}
|
|
345
|
+
const allStepsLength = _allSteps.length;
|
|
346
|
+
const extras = [];
|
|
347
|
+
for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
|
|
348
|
+
const step = _allSteps[allStepsIndex];
|
|
349
|
+
const meta = step.metaKey !== undefined ? metaByMetaKey[step.metaKey] : undefined;
|
|
350
|
+
extras[allStepsIndex] = {
|
|
351
|
+
stopTime,
|
|
352
|
+
meta,
|
|
353
|
+
eventEmitter,
|
|
354
|
+
_bucket: bucket,
|
|
355
|
+
_requestContext: requestContext,
|
|
356
|
+
};
|
|
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;
|
|
373
|
+
}
|
|
374
|
+
continue outerLoop;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
stepLoop: for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
|
|
379
|
+
const step = _allSteps[allStepsIndex];
|
|
380
|
+
const storeEntry = bucket.store.get(step.id);
|
|
381
|
+
try {
|
|
382
|
+
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;
|
|
388
|
+
}
|
|
389
|
+
deps.push(depVal);
|
|
390
|
+
}
|
|
391
|
+
storeEntry[dataIndex] = step.unbatchedExecute(extras[allStepsIndex], ...deps);
|
|
392
|
+
}
|
|
393
|
+
catch (e) {
|
|
394
|
+
bucket.hasErrors = true;
|
|
395
|
+
storeEntry[dataIndex] = (0, error_js_1.newGrafastError)(e, step.id);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return next();
|
|
400
|
+
};
|
|
401
|
+
if (executePromises !== null) {
|
|
402
|
+
const processedPromises = [];
|
|
403
|
+
if (indexesPendingLoopOver.length > 0) {
|
|
404
|
+
// This **must be done in the same tick**
|
|
405
|
+
const promiseOrNot = loopOverResults();
|
|
406
|
+
if ((0, utils_js_1.isPromiseLike)(promiseOrNot)) {
|
|
407
|
+
processedPromises.push(promiseOrNot);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
for (let i = 0, l = executePromises.length; i < l; i++) {
|
|
411
|
+
const executePromise = executePromises[i];
|
|
412
|
+
const index = executePromiseResultIndex[i];
|
|
413
|
+
processedPromises.push(executePromise.then((promiseResult) => {
|
|
414
|
+
results[index] = promiseResult;
|
|
415
|
+
indexesPendingLoopOver.push(index);
|
|
416
|
+
// We must loop over the results in the same tick in which the
|
|
417
|
+
// promise resolved.
|
|
418
|
+
return loopOverResults();
|
|
419
|
+
}));
|
|
420
|
+
}
|
|
421
|
+
return Promise.all(processedPromises).then(runSyncSteps);
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
const promiseOrNot = loopOverResults();
|
|
425
|
+
if ((0, utils_js_1.isPromiseLike)(promiseOrNot)) {
|
|
426
|
+
return promiseOrNot.then(runSyncSteps);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
return runSyncSteps();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const promise = nextPhase(0);
|
|
434
|
+
if ((0, utils_js_1.isPromiseLike)(promise)) {
|
|
435
|
+
return promise.then(executeSamePhaseChildren);
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
return executeSamePhaseChildren();
|
|
439
|
+
}
|
|
440
|
+
// 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);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
return step.execute(size, dependencies, extra);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// Slow mode...
|
|
450
|
+
/**
|
|
451
|
+
* Execute the step, filtering out errors and entries with non-matching
|
|
452
|
+
* polymorphicPaths from the input dependencies and then padding the lists
|
|
453
|
+
* back out at the end.
|
|
454
|
+
*/
|
|
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;
|
|
459
|
+
/** 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];
|
|
464
|
+
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;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (noError) {
|
|
488
|
+
needsNoExecution = false;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
needsNoExecution = false;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
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) {
|
|
500
|
+
// Everything is errors; we can skip execution
|
|
501
|
+
return Object.values(errors);
|
|
502
|
+
}
|
|
503
|
+
else if (foundErrors) {
|
|
504
|
+
const dependenciesWithoutErrors = dependencies.map((depList) => depList.filter((_, index) => !errors[index]));
|
|
505
|
+
const resultWithoutErrors = executeOrStream(step, dependenciesWithoutErrors, extra);
|
|
506
|
+
if ((0, utils_js_1.isPromiseLike)(resultWithoutErrors)) {
|
|
507
|
+
return resultWithoutErrors.then((r) => mergeErrorsBackIn(r, errors, dependencies[0].length));
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
return mergeErrorsBackIn(resultWithoutErrors, errors, dependencies[0].length);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
return reallyExecuteStepWithNoErrors(step, dependencies, extra);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* This function MIGHT throw or reject, so be sure to handle that.
|
|
519
|
+
*/
|
|
520
|
+
function executeStep(step) {
|
|
521
|
+
try {
|
|
522
|
+
const meta = step.metaKey !== undefined ? metaByMetaKey[step.metaKey] : undefined;
|
|
523
|
+
const extra = {
|
|
524
|
+
stopTime,
|
|
525
|
+
meta,
|
|
526
|
+
eventEmitter,
|
|
527
|
+
_bucket: bucket,
|
|
528
|
+
_requestContext: requestContext,
|
|
529
|
+
};
|
|
530
|
+
const dependencies = [];
|
|
531
|
+
const depCount = step.dependencies.length;
|
|
532
|
+
if (depCount > 0 || sideEffectStepsWithErrors !== null) {
|
|
533
|
+
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
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (dev_js_1.isDev &&
|
|
559
|
+
step.layerPlan.reason.type === "polymorphic" &&
|
|
560
|
+
step.polymorphicPaths === null) {
|
|
561
|
+
throw new Error(`GrafastInternalError<c33328fe-6758-4699-8ac6-7be41ce58bfb>: a step without polymorphic paths cannot belong to a polymorphic bucket`);
|
|
562
|
+
}
|
|
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);
|
|
569
|
+
if ((0, utils_js_1.isPromiseLike)(result)) {
|
|
570
|
+
return result.then(null, (error) => {
|
|
571
|
+
// bucket.hasErrors = true;
|
|
572
|
+
return (0, utils_js_1.arrayOfLength)(size, error);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
return result;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
// bucket.hasErrors = true;
|
|
581
|
+
return (0, utils_js_1.arrayOfLength)(size, error);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function executeSamePhaseChildren() {
|
|
585
|
+
// PERF: create a JIT factory for this at planning time
|
|
586
|
+
const childPromises = [];
|
|
587
|
+
// This promise should never reject
|
|
588
|
+
let mutationQueue = null;
|
|
589
|
+
/**
|
|
590
|
+
* Ensures that callback is only called once all other enqueued callbacks
|
|
591
|
+
* are called.
|
|
592
|
+
*/
|
|
593
|
+
const enqueue = (callback) => {
|
|
594
|
+
const result = (mutationQueue ?? Promise.resolve()).then(callback);
|
|
595
|
+
mutationQueue = result.then(noop, noop);
|
|
596
|
+
return result;
|
|
597
|
+
};
|
|
598
|
+
loop: for (const childLayerPlan of childLayerPlans) {
|
|
599
|
+
switch (childLayerPlan.reason.type) {
|
|
600
|
+
case "nullableBoundary":
|
|
601
|
+
case "listItem":
|
|
602
|
+
case "polymorphic": {
|
|
603
|
+
const childBucket = childLayerPlan.newBucket(bucket);
|
|
604
|
+
if (childBucket !== null) {
|
|
605
|
+
// Execute
|
|
606
|
+
const result = executeBucket(childBucket, requestContext);
|
|
607
|
+
if ((0, utils_js_1.isPromiseLike)(result)) {
|
|
608
|
+
childPromises.push(result);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
case "mutationField": {
|
|
614
|
+
const childBucket = childLayerPlan.newBucket(bucket);
|
|
615
|
+
if (childBucket !== null) {
|
|
616
|
+
// Enqueue for execution (mutations must run in order)
|
|
617
|
+
const promise = enqueue(() => executeBucket(childBucket, requestContext));
|
|
618
|
+
childPromises.push(promise);
|
|
619
|
+
}
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
case "subroutine":
|
|
623
|
+
case "subscription":
|
|
624
|
+
case "defer": {
|
|
625
|
+
// Ignore; these are handled elsewhere
|
|
626
|
+
continue loop;
|
|
627
|
+
}
|
|
628
|
+
case "root": {
|
|
629
|
+
throw new Error(
|
|
630
|
+
// *confused emoji*
|
|
631
|
+
"GrafastInternalError<05fb7069-81b5-43f7-ae71-f62547d2c2b7>: root cannot be not the root (...)");
|
|
632
|
+
}
|
|
633
|
+
default: {
|
|
634
|
+
const never = childLayerPlan.reason;
|
|
635
|
+
throw new Error(`GrafastInternalError<c6984a96-050e-4d40-ab18-a8c5dc7e239b>: unhandled reason '${(0, inspect_js_1.inspect)(never)}'`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (childPromises.length > 0) {
|
|
640
|
+
return Promise.all(childPromises).then(() => {
|
|
641
|
+
bucket.isComplete = true;
|
|
642
|
+
return;
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
bucket.isComplete = true;
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
exports.executeBucket = executeBucket;
|
|
652
|
+
/** @internal */
|
|
653
|
+
function newBucket(spec, parentMetaByMetaKey) {
|
|
654
|
+
if (dev_js_1.isDev) {
|
|
655
|
+
// Some validations
|
|
656
|
+
if (!(spec.size > 0)) {
|
|
657
|
+
throw new Error("GrafastInternalError<eb5c962d-c748-4759-95e3-52c50c873593>: No need to create an empty bucket!");
|
|
658
|
+
}
|
|
659
|
+
assert.strictEqual(spec.polymorphicPathList.length, spec.size, "polymorphicPathList length must match bucket size");
|
|
660
|
+
/*
|
|
661
|
+
for (let i = 0, l = spec.size; i < l; i++) {
|
|
662
|
+
const p = spec.polymorphicPathList[i];
|
|
663
|
+
assert.strictEqual(
|
|
664
|
+
typeof p,
|
|
665
|
+
"string",
|
|
666
|
+
`Entry ${i} in polymorphicPathList for bucket for ${spec.layerPlan} was not a string`,
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
*/
|
|
670
|
+
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`);
|
|
673
|
+
}
|
|
674
|
+
if (!spec.iterators) {
|
|
675
|
+
throw new Error(`newBucket called but no iterators array was specified`);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const type = spec.layerPlan.reason.type;
|
|
679
|
+
const metaByMetaKey = parentMetaByMetaKey === null ||
|
|
680
|
+
type === "root" ||
|
|
681
|
+
type === "mutationField" ||
|
|
682
|
+
type === "subscription"
|
|
683
|
+
? // Reset the metaByMetaKey
|
|
684
|
+
spec.layerPlan.operationPlan.makeMetaByMetaKey()
|
|
685
|
+
: parentMetaByMetaKey;
|
|
686
|
+
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,
|
|
694
|
+
metaByMetaKey,
|
|
695
|
+
isComplete: false,
|
|
696
|
+
children: Object.create(null),
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
exports.newBucket = newBucket;
|
|
700
|
+
//# sourceMappingURL=executeBucket.js.map
|