grafast 0.1.1-beta.6 → 0.1.1-beta.7

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 (84) hide show
  1. package/README.md +1 -0
  2. package/dist/dev.d.ts +1 -0
  3. package/dist/dev.js +5 -5
  4. package/dist/engine/LayerPlan.js +140 -67
  5. package/dist/engine/OperationPlan.d.ts +1 -0
  6. package/dist/engine/OperationPlan.js +86 -11
  7. package/dist/engine/OutputPlan.js +14 -8
  8. package/dist/engine/StepTracker.js +55 -15
  9. package/dist/engine/executeBucket.d.ts +5 -1
  10. package/dist/engine/executeBucket.js +545 -167
  11. package/dist/engine/executeOutputPlan.js +6 -0
  12. package/dist/engine/lib/withGlobalLayerPlan.d.ts +2 -0
  13. package/dist/engine/lib/withGlobalLayerPlan.js +5 -1
  14. package/dist/error.d.ts +4 -4
  15. package/dist/error.js +45 -38
  16. package/dist/execute.d.ts +7 -1
  17. package/dist/execute.js +2 -6
  18. package/dist/grafastPrint.d.ts +3 -0
  19. package/dist/grafastPrint.js +63 -1
  20. package/dist/index.d.ts +7 -5
  21. package/dist/index.js +29 -5
  22. package/dist/inspect.d.ts +2 -1
  23. package/dist/interfaces.d.ts +65 -5
  24. package/dist/interfaces.js +20 -1
  25. package/dist/makeGrafastSchema.d.ts +4 -1
  26. package/dist/makeGrafastSchema.js +317 -156
  27. package/dist/mermaid.js +69 -5
  28. package/dist/operationPlan-input.js +1 -1
  29. package/dist/planJSONInterfaces.d.ts +4 -0
  30. package/dist/prepare.js +68 -60
  31. package/dist/step.d.ts +41 -22
  32. package/dist/step.js +95 -72
  33. package/dist/steps/__flag.d.ts +61 -0
  34. package/dist/steps/__flag.js +257 -0
  35. package/dist/steps/__inputDynamicScalar.d.ts +2 -2
  36. package/dist/steps/__inputObject.d.ts +2 -2
  37. package/dist/steps/__item.d.ts +2 -1
  38. package/dist/steps/__item.js +4 -1
  39. package/dist/steps/__trackedValue.d.ts +3 -3
  40. package/dist/steps/__trackedValue.js +5 -2
  41. package/dist/steps/access.d.ts +3 -3
  42. package/dist/steps/access.js +1 -1
  43. package/dist/steps/applyTransforms.js +46 -26
  44. package/dist/steps/condition.d.ts +31 -0
  45. package/dist/steps/condition.js +94 -0
  46. package/dist/steps/connection.d.ts +23 -12
  47. package/dist/steps/connection.js +46 -9
  48. package/dist/steps/constant.d.ts +4 -2
  49. package/dist/steps/constant.js +13 -1
  50. package/dist/steps/error.d.ts +2 -2
  51. package/dist/steps/error.js +4 -3
  52. package/dist/steps/first.d.ts +3 -3
  53. package/dist/steps/first.js +2 -7
  54. package/dist/steps/graphqlResolver.js +51 -25
  55. package/dist/steps/index.d.ts +2 -0
  56. package/dist/steps/index.js +13 -2
  57. package/dist/steps/lambda.d.ts +2 -2
  58. package/dist/steps/last.d.ts +2 -2
  59. package/dist/steps/list.d.ts +3 -3
  60. package/dist/steps/list.js +3 -7
  61. package/dist/steps/listTransform.d.ts +5 -5
  62. package/dist/steps/listTransform.js +45 -21
  63. package/dist/steps/listen.d.ts +2 -5
  64. package/dist/steps/listen.js +8 -10
  65. package/dist/steps/load.d.ts +37 -22
  66. package/dist/steps/load.js +43 -15
  67. package/dist/steps/node.d.ts +2 -2
  68. package/dist/steps/object.d.ts +12 -13
  69. package/dist/steps/object.js +7 -11
  70. package/dist/steps/polymorphicBranch.d.ts +2 -2
  71. package/dist/steps/polymorphicBranch.js +5 -8
  72. package/dist/steps/proxy.d.ts +3 -4
  73. package/dist/steps/proxy.js +5 -2
  74. package/dist/steps/remapKeys.d.ts +3 -3
  75. package/dist/steps/remapKeys.js +4 -2
  76. package/dist/steps/reverse.d.ts +3 -3
  77. package/dist/steps/reverse.js +5 -2
  78. package/dist/steps/sideEffect.d.ts +2 -2
  79. package/dist/subscribe.d.ts +7 -1
  80. package/dist/subscribe.js +2 -6
  81. package/dist/utils.js +22 -2
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/package.json +5 -5
package/README.md CHANGED
@@ -55,6 +55,7 @@ And please give some love to our featured sponsors 🤩:
55
55
  <td align="center"><a href="https://stellate.co/"><img src="https://graphile.org/images/sponsors/Stellate.png" width="90" height="90" alt="Stellate" /><br />Stellate</a> *</td>
56
56
  </tr><tr>
57
57
  <td align="center"><a href="https://gosteelhead.com/"><img src="https://graphile.org/images/sponsors/steelhead.svg" width="90" height="90" alt="Steelhead" /><br />Steelhead</a> *</td>
58
+ <td align="center"><a href="https://www.sylvera.com/"><img src="https://graphile.org/images/sponsors/sylvera.svg" width="90" height="90" alt="Sylvera" /><br />Sylvera</a> *</td>
58
59
  </tr></table>
59
60
 
60
61
  <em>\* Sponsors the entire Graphile suite</em>
package/dist/dev.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export declare const isTest: boolean;
1
2
  export declare function noop(): void;
2
3
  //# sourceMappingURL=dev.d.ts.map
package/dist/dev.js CHANGED
@@ -1,17 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noop = exports.isDev = void 0;
3
+ exports.noop = exports.isTest = exports.isDev = void 0;
4
4
  /**
5
5
  * @internal
6
6
  */
7
7
  const graphileEnv = typeof process !== "undefined" ? process.env.GRAPHILE_ENV : undefined;
8
8
  const nodeEnv = typeof process !== "undefined" ? process.env.NODE_ENV : undefined;
9
+ const mode = graphileEnv !== undefined ? graphileEnv : nodeEnv;
9
10
  /**
10
11
  * @internal
11
12
  */
12
- exports.isDev = graphileEnv !== undefined
13
- ? graphileEnv === "development" || graphileEnv === "test"
14
- : nodeEnv === "development" || nodeEnv === "test";
13
+ exports.isDev = mode === "development" || mode === "test";
14
+ exports.isTest = mode === "test";
15
15
  function noop() { }
16
16
  exports.noop = noop;
17
17
  if (typeof process !== "undefined" &&
@@ -19,7 +19,7 @@ if (typeof process !== "undefined" &&
19
19
  typeof nodeEnv === "undefined") {
20
20
  console.warn(`The GRAPHILE_ENV environmental variable is not set; Grafast will run in production mode. In your development environments, it's recommended that you set \`GRAPHILE_ENV=development\` to opt in to additional checks that will provide guidance and help you to catch issues in your code earlier, and other changes such as formatting to improve your development experience.`);
21
21
  }
22
- else if (exports.isDev) {
22
+ else if (exports.isDev && !exports.isTest) {
23
23
  console.warn(`Grafast is running in development mode due to \`${graphileEnv !== undefined
24
24
  ? `GRAPHILE_ENV=${graphileEnv}`
25
25
  : `NODE_ENV=${nodeEnv}`}\`; this is recommended for development environments (and strongly discouraged in production), but will impact on performance - in particular, planning will be significantly more expensive.`);
@@ -5,8 +5,8 @@ const tslib_1 = require("tslib");
5
5
  const lru_1 = tslib_1.__importDefault(require("@graphile/lru"));
6
6
  const tamedevil_1 = tslib_1.__importDefault(require("tamedevil"));
7
7
  const assert = tslib_1.__importStar(require("../assert.js"));
8
- const error_js_1 = require("../error.js");
9
8
  const inspect_js_1 = require("../inspect.js");
9
+ const interfaces_js_1 = require("../interfaces.js");
10
10
  const polymorphic_js_1 = require("../polymorphic.js");
11
11
  const executeBucket_js_1 = require("./executeBucket.js");
12
12
  function isBranchingLayerPlan(layerPlan) {
@@ -76,8 +76,8 @@ class LayerPlan {
76
76
  */
77
77
  this.rootStep = null;
78
78
  /**
79
- * Which plans the results for which are available in a parent bucket need to
80
- * be "copied across" to this bucket because plans in this bucket still
79
+ * Which steps the results for which are available in a parent bucket need to
80
+ * be "copied across" to this bucket because steps in this bucket still
81
81
  * reference them?
82
82
  *
83
83
  * @internal
@@ -132,7 +132,7 @@ class LayerPlan {
132
132
  const reasonExtra = this.reason.type === "polymorphic"
133
133
  ? `{${this.reason.typeNames.join(",")}}`
134
134
  : "";
135
- const deps = this.copyStepIds.length > 0 ? `%${this.copyStepIds}` : "";
135
+ const deps = this.copyStepIds.length > 0 ? `/${this.copyStepIds}` : "";
136
136
  return `LayerPlan<${this.id}${chain}?${this.reason.type}${reasonExtra}!${this.rootStep?.id ?? "x"}${deps}>`;
137
137
  }
138
138
  print(depth = 0) {
@@ -158,17 +158,18 @@ class LayerPlan {
158
158
  return this.operationPlan._addModifierStep(step);
159
159
  }
160
160
  finalize() {
161
- if (this.reason.type === "nullableBoundary" ||
162
- this.reason.type === "listItem") {
163
- const n = this.copyStepIds.length;
164
- const signature = this.reason.type[0] + n;
165
- withNewBucketFactory(signature, n, this.reason.type, (fn) => {
161
+ if (
162
+ // this.reason.type === "nullableBoundary" ||
163
+ this.reason.type === "listItem") {
164
+ const u = this.copyStepIds.length;
165
+ const signature = `${this.reason.type[0]}_${u}`;
166
+ withNewBucketFactory(signature, u, this.reason.type, (fn) => {
166
167
  this.newBucket = fn(this.copyStepIds);
167
168
  });
168
169
  }
169
170
  }
170
171
  newBucket(parentBucket) {
171
- const copyStepIds = this.copyStepIds;
172
+ const { copyStepIds } = this;
172
173
  const store = new Map();
173
174
  const polymorphicPathList = this.reason.type === "mutationField"
174
175
  ? parentBucket.polymorphicPathList
@@ -178,20 +179,41 @@ class LayerPlan {
178
179
  let size = 0;
179
180
  switch (this.reason.type) {
180
181
  case "nullableBoundary": {
181
- const itemStepId = this.rootStep?.id;
182
- assert.ok(itemStepId != null, "GrafastInternalError<f8136364-46c7-4886-b2ae-51319826f97d>: nullableStepStore layer plan has no rootStepId");
183
- const nullableStepStore = parentBucket.store.get(itemStepId);
184
- if (!nullableStepStore) {
185
- throw new Error(`GrafastInternalError<017dc8bf-1db1-4983-a41e-e69c6652e4c7>: could not find entry '${itemStepId}' (${parentBucket.layerPlan.operationPlan.dangerouslyGetStep(itemStepId)}) in store for ${parentBucket.layerPlan}`);
182
+ if (this.rootStep == null) {
183
+ throw new Error("GrafastInternalError<f8136364-46c7-4886-b2ae-51319826f97d>: nullableStepStore layer plan has no rootStepId");
186
184
  }
185
+ const itemStepId = this.rootStep.id;
187
186
  // PERF: if parent bucket has no nulls/errors in `itemStepId`
188
187
  // then we can just copy everything wholesale rather than building
189
188
  // new arrays and looping.
190
189
  const hasNoNullsOrErrors = false;
191
- if (hasNoNullsOrErrors) {
190
+ if (this.rootStep._isUnary) {
191
+ const fieldValue = parentBucket.store.get(itemStepId);
192
+ const forbiddenFlags = fieldValue._entryFlags & interfaces_js_1.FORBIDDEN_BY_NULLABLE_BOUNDARY_FLAGS;
193
+ if (forbiddenFlags) {
194
+ size = 0;
195
+ }
196
+ else {
197
+ size = parentBucket.size;
198
+ store.set(itemStepId, fieldValue);
199
+ for (const stepId of copyStepIds) {
200
+ store.set(stepId, parentBucket.store.get(stepId));
201
+ }
202
+ for (let i = 0; i < size; i++) {
203
+ map.set(i, i);
204
+ polymorphicPathList[i] = parentBucket.polymorphicPathList[i];
205
+ iterators[i] = parentBucket.iterators[i];
206
+ }
207
+ }
208
+ }
209
+ else if (hasNoNullsOrErrors) {
210
+ const nullableStepStore = parentBucket.store.get(itemStepId);
211
+ if (!nullableStepStore) {
212
+ throw new Error(`GrafastInternalError<017dc8bf-1db1-4983-a41e-e69c6652e4c7>: could not find entry '${itemStepId}' (${parentBucket.layerPlan.operationPlan.dangerouslyGetStep(itemStepId)}) in store for ${parentBucket.layerPlan}`);
213
+ }
192
214
  store.set(itemStepId, nullableStepStore);
193
- for (const planId of copyStepIds) {
194
- store.set(planId, parentBucket.store.get(planId));
215
+ for (const stepId of copyStepIds) {
216
+ store.set(stepId, parentBucket.store.get(stepId));
195
217
  }
196
218
  for (let originalIndex = 0; originalIndex < parentBucket.size; originalIndex++) {
197
219
  const newIndex = size++;
@@ -203,16 +225,26 @@ class LayerPlan {
203
225
  }
204
226
  else {
205
227
  const itemStepIdList = [];
206
- store.set(itemStepId, itemStepIdList);
207
- // Prepare store with an empty list for each copyPlanId
208
- for (const planId of copyStepIds) {
209
- store.set(planId, []);
228
+ store.set(itemStepId, (0, executeBucket_js_1.batchExecutionValue)(itemStepIdList));
229
+ for (const stepId of copyStepIds) {
230
+ const ev = parentBucket.store.get(stepId);
231
+ if (ev.isBatch) {
232
+ // Prepare store with an empty list for each copyPlanId
233
+ store.set(stepId, (0, executeBucket_js_1.batchExecutionValue)([]));
234
+ }
235
+ else {
236
+ store.set(stepId, ev);
237
+ }
238
+ }
239
+ const nullableStepStore = parentBucket.store.get(itemStepId);
240
+ if (!nullableStepStore) {
241
+ throw new Error(`GrafastInternalError<017dc8bf-1db1-4983-a41e-e69c6652e4c7>: could not find entry '${itemStepId}' (${parentBucket.layerPlan.operationPlan.dangerouslyGetStep(itemStepId)}) in store for ${parentBucket.layerPlan}`);
210
242
  }
211
243
  // We'll typically be creating fewer nullableBoundary bucket entries
212
244
  // than we have parent bucket entries (because we exclude nulls), so
213
245
  // we must "multiply up" (down) the store entries.
214
246
  for (let originalIndex = 0; originalIndex < parentBucket.size; originalIndex++) {
215
- const fieldValue = nullableStepStore[originalIndex];
247
+ const fieldValue = nullableStepStore.at(originalIndex);
216
248
  if (fieldValue != null) {
217
249
  const newIndex = size++;
218
250
  map.set(originalIndex, newIndex);
@@ -220,9 +252,12 @@ class LayerPlan {
220
252
  polymorphicPathList[newIndex] =
221
253
  parentBucket.polymorphicPathList[originalIndex];
222
254
  iterators[newIndex] = parentBucket.iterators[originalIndex];
223
- for (const planId of copyStepIds) {
224
- store.get(planId)[newIndex] =
225
- parentBucket.store.get(planId)[originalIndex];
255
+ for (const stepId of copyStepIds) {
256
+ const ev = store.get(stepId);
257
+ if (ev.isBatch) {
258
+ const orig = parentBucket.store.get(stepId);
259
+ ev._copyResult(newIndex, orig, originalIndex);
260
+ }
226
261
  }
227
262
  }
228
263
  }
@@ -235,32 +270,52 @@ class LayerPlan {
235
270
  if (!listStepStore) {
236
271
  throw new Error(`GrafastInternalError<314865b0-f7e8-4e81-b966-56e5a0de562e>: could not find entry '${listStepId}' (${parentBucket.layerPlan.operationPlan.dangerouslyGetStep(listStepId)}) in store for layerPlan ${parentBucket.layerPlan}`);
237
272
  }
238
- const itemStepId = this.rootStep?.id;
239
- if (itemStepId == null) {
273
+ if (this.rootStep == null) {
240
274
  throw new Error("GrafastInternalError<b3a2bff9-15c6-47e2-aa82-19c862324f1a>: listItem layer plan has no rootStepId");
241
275
  }
242
- store.set(itemStepId, []);
243
- // Prepare store with an empty list for each copyPlanId
244
- for (const planId of copyStepIds) {
245
- store.set(planId, []);
276
+ const itemStepId = this.rootStep.id;
277
+ // Item steps are **NOT** unary
278
+ const itemStepIdList = this.rootStep._isUnary ? null : [];
279
+ if (this.rootStep._isUnary) {
280
+ // handled later
281
+ const list = listStepStore.at(0);
282
+ store.set(itemStepId, (0, executeBucket_js_1.unaryExecutionValue)(Array.isArray(list) ? list[0] : list));
283
+ }
284
+ else {
285
+ store.set(itemStepId, (0, executeBucket_js_1.batchExecutionValue)(itemStepIdList));
286
+ }
287
+ for (const stepId of copyStepIds) {
288
+ const ev = parentBucket.store.get(stepId);
289
+ if (ev.isBatch) {
290
+ // Prepare store with an empty list for each copyPlanId
291
+ store.set(stepId, (0, executeBucket_js_1.batchExecutionValue)([]));
292
+ }
293
+ else {
294
+ store.set(stepId, ev);
295
+ }
246
296
  }
247
297
  // We'll typically be creating more listItem bucket entries than we
248
298
  // have parent buckets, so we must "multiply up" the store entries.
249
299
  for (let originalIndex = 0; originalIndex < parentBucket.size; originalIndex++) {
250
- const list = listStepStore[originalIndex];
300
+ const list = listStepStore.at(originalIndex);
251
301
  if (Array.isArray(list)) {
252
302
  const newIndexes = [];
253
303
  map.set(originalIndex, newIndexes);
254
304
  for (let j = 0, l = list.length; j < l; j++) {
255
305
  const newIndex = size++;
256
306
  newIndexes.push(newIndex);
257
- store.get(itemStepId)[newIndex] = list[j];
307
+ if (itemStepIdList !== null) {
308
+ itemStepIdList[newIndex] = list[j];
309
+ }
258
310
  polymorphicPathList[newIndex] =
259
311
  parentBucket.polymorphicPathList[originalIndex];
260
312
  iterators[newIndex] = parentBucket.iterators[originalIndex];
261
- for (const planId of copyStepIds) {
262
- store.get(planId)[newIndex] =
263
- parentBucket.store.get(planId)[originalIndex];
313
+ for (const stepId of copyStepIds) {
314
+ const ev = store.get(stepId);
315
+ if (ev.isBatch) {
316
+ const orig = parentBucket.store.get(stepId);
317
+ ev._copyResult(newIndex, orig, originalIndex);
318
+ }
264
319
  }
265
320
  }
266
321
  }
@@ -273,8 +328,8 @@ class LayerPlan {
273
328
  for (let i = 0; i < parentBucket.size; i++) {
274
329
  map.set(i, i);
275
330
  }
276
- for (const planId of copyStepIds) {
277
- store.set(planId, parentBucket.store.get(planId));
331
+ for (const stepId of copyStepIds) {
332
+ store.set(stepId, parentBucket.store.get(stepId));
278
333
  }
279
334
  break;
280
335
  }
@@ -287,20 +342,24 @@ class LayerPlan {
287
342
  // We're only copying over the entries that match this type (note:
288
343
  // they may end up being null, but that's okay)
289
344
  const targetTypeNames = this.reason.typeNames;
290
- for (const planId of copyStepIds) {
291
- store.set(planId, []);
292
- if (!parentBucket.store.has(planId)) {
293
- throw new Error(`GrafastInternalError<548f0d84-4556-4189-8655-fb16aa3345a6>: new bucket for ${this} wants to copy ${this.operationPlan.dangerouslyGetStep(planId)}, but bucket for ${parentBucket.layerPlan} doesn't contain that plan`);
345
+ for (const stepId of copyStepIds) {
346
+ const ev = parentBucket.store.get(stepId);
347
+ if (!ev) {
348
+ throw new Error(`GrafastInternalError<548f0d84-4556-4189-8655-fb16aa3345a6>: new bucket for ${this} wants to copy ${this.operationPlan.dangerouslyGetStep(stepId)}, but bucket for ${parentBucket.layerPlan} doesn't contain that plan`);
349
+ }
350
+ if (ev.isBatch) {
351
+ store.set(stepId, (0, executeBucket_js_1.batchExecutionValue)([]));
352
+ }
353
+ else {
354
+ store.set(stepId, ev);
294
355
  }
295
356
  }
296
357
  for (let originalIndex = 0; originalIndex < parentBucket.size; originalIndex++) {
297
- const value = polymorphicPlanStore[originalIndex];
298
- if (value == null) {
299
- continue;
300
- }
301
- if ((0, error_js_1.isGrafastError)(value)) {
358
+ const flags = polymorphicPlanStore._flagsAt(originalIndex);
359
+ if (flags & (interfaces_js_1.FLAG_ERROR | interfaces_js_1.FLAG_INHIBITED | interfaces_js_1.FLAG_NULL)) {
302
360
  continue;
303
361
  }
362
+ const value = polymorphicPlanStore.at(originalIndex);
304
363
  const typeName = (0, polymorphic_js_1.resolveType)(value);
305
364
  if (!targetTypeNames.includes(typeName)) {
306
365
  continue;
@@ -314,8 +373,11 @@ class LayerPlan {
314
373
  polymorphicPathList[newIndex] = newPolymorphicPath;
315
374
  iterators[newIndex] = parentBucket.iterators[originalIndex];
316
375
  for (const planId of copyStepIds) {
317
- store.get(planId)[newIndex] =
318
- parentBucket.store.get(planId)[originalIndex];
376
+ const ev = store.get(planId);
377
+ if (ev.isBatch) {
378
+ const orig = parentBucket.store.get(planId);
379
+ ev._copyResult(newIndex, orig, originalIndex);
380
+ }
319
381
  }
320
382
  }
321
383
  break;
@@ -345,7 +407,7 @@ class LayerPlan {
345
407
  size,
346
408
  store,
347
409
  // PERF: not necessarily, if we don't copy the errors, we don't have the errors.
348
- hasErrors: parentBucket.hasErrors,
410
+ flagUnion: parentBucket.flagUnion,
349
411
  polymorphicPathList,
350
412
  iterators,
351
413
  }, parentBucket.metaByMetaKey);
@@ -369,8 +431,9 @@ const te_parentBucketDotPolymorphicPathList = (0, tamedevil_1.default) `parentBu
369
431
  const te_parentBucketDotIterators = (0, tamedevil_1.default) `parentBucket.iterators`;
370
432
  const te_emptyArray = (0, tamedevil_1.default) `[]`;
371
433
  const ref_newBucket = tamedevil_1.default.ref(executeBucket_js_1.newBucket, "newBucket");
434
+ const ref_batchExecutionValue = tamedevil_1.default.ref(executeBucket_js_1.batchExecutionValue, "batchExecutionValue");
372
435
  function makeNewBucketExpression(signature, reasonType, inner) {
373
- return (0, tamedevil_1.default) `\
436
+ const expr = (0, tamedevil_1.default) `\
374
437
  (function ${tamedevil_1.default.identifier(`newBucket_${signature}`)}(parentBucket) {
375
438
  const store = new Map();
376
439
  const polymorphicPathList = ${reasonType === "mutationField"
@@ -389,7 +452,7 @@ ${inner}
389
452
  size,
390
453
  store,
391
454
  // PERF: not necessarily, if we don't copy the errors, we don't have the errors.
392
- hasErrors: parentBucket.hasErrors,
455
+ flagUnion: parentBucket.flagUnion,
393
456
  polymorphicPathList,
394
457
  iterators,
395
458
  }, parentBucket.metaByMetaKey);
@@ -401,9 +464,14 @@ ${inner}
401
464
  return null;
402
465
  }
403
466
  })`;
467
+ // te.debug(expr);
468
+ return expr;
404
469
  }
405
470
  function newBucketFactoryInnerExpression(signature, copyCount, reasonType) {
406
471
  if (reasonType === "nullableBoundary") {
472
+ if (Math.random() < 2) {
473
+ throw new Error("This code no longer works since we added unary steps.");
474
+ }
407
475
  // PERF: if parent bucket has no nulls/errors in itemStepId
408
476
  // then we can just copy everything wholesale rather than building
409
477
  // new arrays and looping.
@@ -414,12 +482,14 @@ function newBucketFactoryInnerExpression(signature, copyCount, reasonType) {
414
482
  const te_target = tamedevil_1.default.identifier(`target${i}`);
415
483
  const te_i = tamedevil_1.default.lit(i);
416
484
  blocks.push((0, tamedevil_1.default) `\
417
- const ${te_source} = parentBucket.store.get(copyPlanIds[${te_i}]);
418
- const ${te_target} = [];
419
- store.set(copyPlanIds[${te_i}], ${te_target});
485
+ const ${te_source} = parentBucket.store.get(copyStepIds[${te_i}]);
486
+ const ${te_target} = ${te_source}.isBatch ? ${ref_batchExecutionValue}([]) : ${te_source};
487
+ store.set(copyStepIds[${te_i}], ${te_target});
420
488
  `);
421
489
  copyBlocks.push((0, tamedevil_1.default) `\
422
- ${te_target}[newIndex] = ${te_source}[originalIndex];
490
+ if(${te_target}.isBatch) {
491
+ ${te_target}.entries[newIndex] = ${te_source}.at(originalIndex);
492
+ }
423
493
  `);
424
494
  }
425
495
  return makeNewBucketExpression(signature, reasonType, (0, tamedevil_1.default) `\
@@ -427,7 +497,7 @@ function newBucketFactoryInnerExpression(signature, copyCount, reasonType) {
427
497
  const nullableStepStore = parentBucket.store.get(itemStepId);
428
498
 
429
499
  const itemStepIdList = [];
430
- store.set(itemStepId, itemStepIdList);
500
+ store.set(itemStepId, ${ref_batchExecutionValue}(itemStepIdList));
431
501
 
432
502
  // Prepare store with an empty list for each copyPlanId
433
503
  ${tamedevil_1.default.join(blocks, "")}
@@ -440,7 +510,7 @@ ${tamedevil_1.default.join(blocks, "")}
440
510
  originalIndex < parentBucket.size;
441
511
  originalIndex++
442
512
  ) {
443
- const fieldValue = nullableStepStore[originalIndex];
513
+ const fieldValue = nullableStepStore.at(originalIndex);
444
514
  if (fieldValue != null) {
445
515
  const newIndex = size++;
446
516
  map.set(originalIndex, newIndex);
@@ -461,19 +531,22 @@ ${tamedevil_1.default.join(copyBlocks, "")}
461
531
  const te_target = tamedevil_1.default.identifier(`target${i}`);
462
532
  const te_i = tamedevil_1.default.lit(i);
463
533
  blocks.push((0, tamedevil_1.default) `\
464
- const ${te_source} = parentBucket.store.get(copyPlanIds[${te_i}]);
465
- const ${te_target} = [];
466
- store.set(copyPlanIds[${te_i}], ${te_target});
534
+ const ${te_source} = parentBucket.store.get(copyStepIds[${te_i}]);
535
+ const ${te_target} = ${te_source}.isBatch ? ${ref_batchExecutionValue}([]) : ${te_source};
536
+ store.set(copyStepIds[${te_i}], ${te_target});
467
537
  `);
468
538
  copyBlocks.push((0, tamedevil_1.default) `\
469
- ${te_target}[newIndex] = ${te_source}[originalIndex];
539
+ if (${te_target}.isBatch) {
540
+ ${te_target}.entries[newIndex] = ${te_source}.at(originalIndex);
541
+ }
470
542
  `);
471
543
  }
472
544
  return makeNewBucketExpression(signature, reasonType, (0, tamedevil_1.default) `\
473
- const listStepStore = parentBucket.store.get(this.reason.parentStep.id);
545
+ const listStepId = this.reason.parentStep.id;
546
+ const listStepStore = parentBucket.store.get(listStepId);
474
547
 
475
548
  const itemStepIdList = [];
476
- store.set(this.rootStep.id, itemStepIdList);
549
+ store.set(this.rootStep.id, ${ref_batchExecutionValue}(itemStepIdList));
477
550
 
478
551
  // Prepare store with an empty list for each copyPlanId
479
552
  ${tamedevil_1.default.join(blocks, "")}
@@ -485,7 +558,7 @@ ${tamedevil_1.default.join(copyBlocks, "")}
485
558
  originalIndex < parentBucket.size;
486
559
  originalIndex++
487
560
  ) {
488
- const list = listStepStore[originalIndex];
561
+ const list = listStepStore.at(originalIndex);
489
562
  if (Array.isArray(list)) {
490
563
  const newIndexes = [];
491
564
  map.set(originalIndex, newIndexes);
@@ -522,7 +595,7 @@ function withNewBucketFactory(signature, copyCount, reasonType, callback) {
522
595
  makingNewBucketCallbacks.set(signature, callbacks);
523
596
  const executorExpression = newBucketFactoryInnerExpression(signature, copyCount, reasonType);
524
597
  const factoryExpression = (0, tamedevil_1.default) `\
525
- function ${tamedevil_1.default.identifier(`layerPlanNewBucketFactory_${signature}`)}(copyPlanIds) {
598
+ function ${tamedevil_1.default.identifier(`layerPlanNewBucketFactory_${signature}`)}(copyStepIds) {
526
599
  return ${executorExpression};
527
600
  }`;
528
601
  tamedevil_1.default.runInBatch(factoryExpression, (factory) => {
@@ -175,6 +175,7 @@ export declare class OperationPlan {
175
175
  * know all our dependent's needs before we optimise ourself.
176
176
  */
177
177
  private optimizeSteps;
178
+ private inlineSteps;
178
179
  /** Finalizes each step */
179
180
  private finalizeSteps;
180
181
  private finalizeLayerPlans;
@@ -208,6 +208,9 @@ class OperationPlan {
208
208
  });
209
209
  this.checkTimeout();
210
210
  this.lap("optimizeSteps");
211
+ this.inlineSteps();
212
+ this.checkTimeout();
213
+ this.lap("inlineSteps");
211
214
  this.phase = "finalize";
212
215
  this.stepTracker.finalizeSteps();
213
216
  // Get rid of steps that are no longer needed after optimising outputPlans
@@ -810,7 +813,7 @@ class OperationPlan {
810
813
  });
811
814
  const $__item = this.itemStepForListStep(parentLayerPlan, $step, listDepth);
812
815
  const $item = (0, step_js_1.isListCapableStep)($step)
813
- ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)($__item.layerPlan, polymorphicPaths, $step.listItem, $step, $__item)
816
+ ? (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)($__item.layerPlan, $__item.polymorphicPaths, $step.listItem, $step, $__item)
814
817
  : $__item;
815
818
  this.planIntoOutputPlan(listOutputPlan, $item.layerPlan, path, polymorphicPath, polymorphicPaths, selections, null, null, nullableFieldType.ofType, $item, locationDetails, listDepth + 1);
816
819
  }
@@ -862,7 +865,7 @@ class OperationPlan {
862
865
  }
863
866
  }
864
867
  catch (e) {
865
- throw new Error(`The step returned by '${path.join(".")}' is not compatible with the GraphQL object type '${nullableFieldType.name}': ${e.message}`, { cause: e });
868
+ throw new Error(`The step returned by '${path.join(".")}${"[]".repeat(listDepth)}' is not compatible with the GraphQL object type '${nullableFieldType.name}': ${e.message}`, { cause: e });
866
869
  }
867
870
  }
868
871
  if (!selections) {
@@ -1353,7 +1356,7 @@ class OperationPlan {
1353
1356
  if (step.hasSideEffects) {
1354
1357
  return EMPTY_ARRAY;
1355
1358
  }
1356
- const { dependencies: deps, layerPlan: layerPlan, constructor: stepConstructor, } = (0, utils_js_1.sudo)(step);
1359
+ const { dependencies: deps, dependencyForbiddenFlags: flags, dependencyOnReject: onReject, layerPlan: layerPlan, constructor: stepConstructor, } = (0, utils_js_1.sudo)(step);
1357
1360
  const dependencyCount = deps.length;
1358
1361
  if (dependencyCount === 0) {
1359
1362
  let allPeers = null;
@@ -1384,15 +1387,18 @@ class OperationPlan {
1384
1387
  }
1385
1388
  const minDepth = Math.max(deferBoundaryDepth, dep.layerPlan.depth);
1386
1389
  let allPeers = null;
1387
- for (const { dependencyIndex: peerDependencyIndex, step: possiblyPeer, } of dep.dependents) {
1388
- const { layerPlan: peerLayerPlan } = possiblyPeer;
1390
+ for (const { dependencyIndex: peerDependencyIndex, step: rawPossiblyPeer, } of dep.dependents) {
1391
+ const possiblyPeer = (0, utils_js_1.sudo)(rawPossiblyPeer);
1392
+ const { layerPlan: peerLayerPlan, dependencyForbiddenFlags: peerFlags, dependencyOnReject: peerOnReject, } = possiblyPeer;
1389
1393
  if (possiblyPeer !== step &&
1390
1394
  peerDependencyIndex === 0 &&
1391
1395
  !possiblyPeer.hasSideEffects &&
1392
1396
  possiblyPeer.constructor === stepConstructor &&
1393
1397
  peerLayerPlan.depth >= minDepth &&
1394
1398
  (0, utils_js_1.sudo)(possiblyPeer).dependencies.length === dependencyCount &&
1395
- peerLayerPlan === ancestry[peerLayerPlan.depth]) {
1399
+ peerLayerPlan === ancestry[peerLayerPlan.depth] &&
1400
+ peerFlags[0] === flags[0] &&
1401
+ peerOnReject[0] === onReject[0]) {
1396
1402
  if (allPeers === null) {
1397
1403
  allPeers = [possiblyPeer];
1398
1404
  }
@@ -1428,13 +1434,17 @@ class OperationPlan {
1428
1434
  // Check the final dependency - this is likely to have the fewest
1429
1435
  // dependents (since it was added last it's more likely to be
1430
1436
  // unique).
1431
- for (const { dependencyIndex: peerDependencyIndex, step: possiblyPeer, } of dep.dependents) {
1437
+ for (const { dependencyIndex: peerDependencyIndex, step: rawPossiblyPeer, } of dep.dependents) {
1438
+ const possiblyPeer = (0, utils_js_1.sudo)(rawPossiblyPeer);
1439
+ const { layerPlan: peerLayerPlan, dependencyForbiddenFlags: peerFlags, dependencyOnReject: peerOnReject, dependencies: peerDependencies, } = possiblyPeer;
1432
1440
  if (possiblyPeer !== step &&
1433
1441
  peerDependencyIndex === dependencyIndex &&
1434
1442
  !possiblyPeer.hasSideEffects &&
1435
1443
  possiblyPeer.constructor === stepConstructor &&
1436
- (0, utils_js_1.sudo)(possiblyPeer).dependencies.length === dependencyCount &&
1437
- possiblyPeer.layerPlan === ancestry[possiblyPeer.layerPlan.depth]) {
1444
+ peerDependencies.length === dependencyCount &&
1445
+ peerLayerPlan === ancestry[peerLayerPlan.depth] &&
1446
+ peerFlags[0] === flags[0] &&
1447
+ peerOnReject[0] === onReject[0]) {
1438
1448
  possiblePeers.push(possiblyPeer);
1439
1449
  }
1440
1450
  }
@@ -1533,6 +1543,9 @@ class OperationPlan {
1533
1543
  // unsafe. Should we do so, we should remove this.
1534
1544
  break;
1535
1545
  }
1546
+ else if (step instanceof index_js_1.__FlagStep) {
1547
+ break;
1548
+ }
1536
1549
  else {
1537
1550
  return;
1538
1551
  }
@@ -1570,6 +1583,9 @@ class OperationPlan {
1570
1583
  // break;
1571
1584
  return;
1572
1585
  }
1586
+ else if (step instanceof index_js_1.__FlagStep) {
1587
+ break;
1588
+ }
1573
1589
  else {
1574
1590
  // Plans that rely on external state shouldn't be hoisted because
1575
1591
  // their results may change after a mutation, so the mutation should
@@ -2066,6 +2082,57 @@ class OperationPlan {
2066
2082
  .slice(0, 10)
2067
2083
  .join(", ")}`);
2068
2084
  }
2085
+ inlineSteps() {
2086
+ flagLoop: for (const $flag of this.stepTracker.activeSteps) {
2087
+ if ($flag instanceof index_js_1.__FlagStep) {
2088
+ // We can only inline it if it's not used by an output plan or layer plan
2089
+ {
2090
+ const usages = this.stepTracker.outputPlansByRootStep.get($flag);
2091
+ if (usages?.size) {
2092
+ continue;
2093
+ }
2094
+ }
2095
+ {
2096
+ const usages = this.stepTracker.layerPlansByRootStep.get($flag);
2097
+ if (usages?.size) {
2098
+ continue;
2099
+ }
2100
+ }
2101
+ {
2102
+ const usages = this.stepTracker.layerPlansByParentStep.get($flag);
2103
+ if (usages?.size) {
2104
+ continue;
2105
+ }
2106
+ }
2107
+ // We're only going to inline one if we can inline all.
2108
+ const toInline = [];
2109
+ for (const dependent of $flag.dependents) {
2110
+ const { step, dependencyIndex } = dependent;
2111
+ const $dependent = (0, utils_js_1.sudo)(step);
2112
+ const inlineDetails = $flag.inline($dependent.getDepOptions(dependencyIndex));
2113
+ if (inlineDetails === null) {
2114
+ continue flagLoop;
2115
+ }
2116
+ toInline.push({
2117
+ $dependent,
2118
+ dependencyIndex,
2119
+ inlineDetails,
2120
+ dependent,
2121
+ });
2122
+ }
2123
+ // All of them pass the check, let's inline them
2124
+ for (const todo of toInline) {
2125
+ const { $dependent, dependencyIndex, inlineDetails: { onReject, acceptFlags = interfaces_js_1.DEFAULT_ACCEPT_FLAGS }, } = todo;
2126
+ (0, utils_js_1.writeableArray)($dependent.dependencyOnReject)[dependencyIndex] =
2127
+ onReject;
2128
+ (0, utils_js_1.writeableArray)($dependent.dependencyForbiddenFlags)[dependencyIndex] =
2129
+ interfaces_js_1.ALL_FLAGS & ~acceptFlags;
2130
+ }
2131
+ const $flagDep = (0, utils_js_1.sudo)($flag).dependencies[0];
2132
+ this.stepTracker.replaceStep($flag, $flagDep);
2133
+ }
2134
+ }
2135
+ }
2069
2136
  /** Finalizes each step */
2070
2137
  finalizeSteps() {
2071
2138
  const initialStepCount = this.stepTracker.stepCount;
@@ -2092,9 +2159,13 @@ class OperationPlan {
2092
2159
  if (currentLayerPlan.copyStepIds.includes(dep.id)) {
2093
2160
  break;
2094
2161
  }
2095
- if (index_js_1.isDev && this.stepTracker.getStepById(dep.id) !== dep) {
2162
+ const targetStep = this.stepTracker.getStepById(dep.id);
2163
+ if (index_js_1.isDev && targetStep !== dep) {
2096
2164
  throw new Error(`GrafastInternalError<b291b110-396a-4c9c-814a-5466af3c50e8>: Plan mismatch; are we using a replaced step? Step ID: ${dep.id}; step: ${dep}; stepById: ${this.stepTracker.getStepById(dep.id)}`);
2097
2165
  }
2166
+ if (targetStep._isUnary) {
2167
+ targetStep._isUnaryLocked = true;
2168
+ }
2098
2169
  currentLayerPlan.copyStepIds.push(dep.id);
2099
2170
  currentLayerPlan = currentLayerPlan.parentLayerPlan;
2100
2171
  if (!currentLayerPlan) {
@@ -2312,12 +2383,16 @@ class OperationPlan {
2312
2383
  generatePlanJSON() {
2313
2384
  function printStep(step) {
2314
2385
  const metaString = step.toStringMeta();
2386
+ const sstep = (0, utils_js_1.sudo)(step);
2315
2387
  return {
2316
2388
  id: step.id,
2317
2389
  stepClass: step.constructor.name,
2318
2390
  metaString: metaString ? (0, index_js_1.stripAnsi)(metaString) : metaString,
2391
+ isUnary: step._isUnary,
2319
2392
  bucketId: step.layerPlan.id,
2320
- dependencyIds: (0, utils_js_1.sudo)(step).dependencies.map((d) => d.id),
2393
+ dependencyIds: sstep.dependencies.map((d) => d.id),
2394
+ dependencyForbiddenFlags: sstep.dependencyForbiddenFlags.slice(),
2395
+ dependencyOnReject: sstep.dependencyOnReject.map((or) => or ? String(or) : or),
2321
2396
  polymorphicPaths: step.polymorphicPaths
2322
2397
  ? [...step.polymorphicPaths]
2323
2398
  : undefined,