grafast 0.1.1-beta.11 → 0.1.1-beta.13

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 (41) hide show
  1. package/README.md +0 -3
  2. package/dist/args.js +8 -2
  3. package/dist/constraints.d.ts +15 -1
  4. package/dist/constraints.js +40 -1
  5. package/dist/engine/LayerPlan.d.ts +1 -0
  6. package/dist/engine/LayerPlan.js +62 -9
  7. package/dist/engine/OperationPlan.js +372 -274
  8. package/dist/engine/OutputPlan.d.ts +6 -5
  9. package/dist/engine/OutputPlan.js +423 -444
  10. package/dist/engine/executeBucket.js +36 -77
  11. package/dist/error.d.ts +13 -0
  12. package/dist/grafastGraphql.d.ts +5 -5
  13. package/dist/graphqlCollectFields.d.ts +6 -0
  14. package/dist/graphqlCollectFields.js +14 -8
  15. package/dist/interfaces.d.ts +18 -0
  16. package/dist/interfaces.js +0 -2
  17. package/dist/mermaid.js +20 -11
  18. package/dist/operationPlan-input.js +8 -1
  19. package/dist/planJSONInterfaces.d.ts +2 -0
  20. package/dist/prepare.js +3 -3
  21. package/dist/step.d.ts +5 -5
  22. package/dist/step.js +53 -2
  23. package/dist/steps/__flag.d.ts +3 -2
  24. package/dist/steps/__inputObject.d.ts +1 -0
  25. package/dist/steps/__inputObject.js +36 -0
  26. package/dist/steps/__item.d.ts +3 -3
  27. package/dist/steps/__trackedValue.d.ts +8 -0
  28. package/dist/steps/__trackedValue.js +40 -0
  29. package/dist/steps/__value.d.ts +2 -1
  30. package/dist/steps/constant.d.ts +2 -0
  31. package/dist/steps/constant.js +12 -0
  32. package/dist/steps/graphqlResolver.js +8 -5
  33. package/dist/steps/listen.d.ts +2 -2
  34. package/dist/steps/object.d.ts +1 -1
  35. package/dist/steps/sideEffect.d.ts +0 -1
  36. package/dist/thereCanBeOnlyOne.js +1 -1
  37. package/dist/utils.d.ts +4 -0
  38. package/dist/utils.js +7 -0
  39. package/dist/version.d.ts +1 -1
  40. package/dist/version.js +1 -1
  41. package/package.json +1 -1
@@ -11,8 +11,6 @@ const interfaces_js_1 = require("../interfaces.js");
11
11
  const step_js_1 = require("../step.js");
12
12
  const timeSource_js_1 = require("../timeSource.js");
13
13
  const utils_js_1 = require("../utils.js");
14
- /** Path to use when there's no polymorphic paths. */
15
- const NO_POLY_PATH = "";
16
14
  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 })));
17
15
  function noop() {
18
16
  /*noop*/
@@ -66,7 +64,6 @@ function executeBucket(bucket, requestContext) {
66
64
  const { middleware } = args;
67
65
  const { metaByMetaKey, size, store, layerPlan: { phases, children: childLayerPlans }, } = bucket;
68
66
  const phaseCount = phases.length;
69
- let sideEffectStepsWithErrors = null;
70
67
  // Like a `for(i = 0; i < phaseCount; i++)` loop with some `await`s in it, except it does promise
71
68
  // handling manually so that it can complete synchronously (no promises) if
72
69
  // possible.
@@ -83,7 +80,6 @@ function executeBucket(bucket, requestContext) {
83
80
  */
84
81
  let indexesPendingLoopOver = [];
85
82
  let executePromises = null;
86
- let sideEffectSteps = null;
87
83
  let executePromiseResultIndex = null;
88
84
  const resultList = [];
89
85
  if (phase.checkTimeout &&
@@ -107,14 +103,6 @@ function executeBucket(bucket, requestContext) {
107
103
  const normalSteps = phase.normalSteps;
108
104
  for (let normalStepIndex = 0, l = normalSteps.length; normalStepIndex < l; normalStepIndex++) {
109
105
  const step = normalSteps[normalStepIndex].step;
110
- if (step.hasSideEffects) {
111
- if (sideEffectSteps === null) {
112
- sideEffectSteps = [step];
113
- }
114
- else {
115
- sideEffectSteps.push(step);
116
- }
117
- }
118
106
  try {
119
107
  const r = executeStep(step);
120
108
  if ((0, utils_js_1.isPromiseLike)(r)) {
@@ -144,24 +132,6 @@ function executeBucket(bucket, requestContext) {
144
132
  }
145
133
  }
146
134
  }
147
- const handleSideEffectSteps = () => {
148
- if (!sideEffectStepsWithErrors) {
149
- sideEffectStepsWithErrors = bucket.polymorphicPathList.reduce((memo, path) => {
150
- memo[path ?? NO_POLY_PATH] = [];
151
- return memo;
152
- }, Object.create(null));
153
- }
154
- for (const step of sideEffectSteps) {
155
- if (step.polymorphicPaths) {
156
- for (const path of step.polymorphicPaths) {
157
- sideEffectStepsWithErrors[path].push(step);
158
- }
159
- }
160
- else {
161
- sideEffectStepsWithErrors[NO_POLY_PATH].push(step);
162
- }
163
- }
164
- };
165
135
  const next = () => {
166
136
  return nextPhase(phaseIndex + 1);
167
137
  };
@@ -340,10 +310,6 @@ function executeBucket(bucket, requestContext) {
340
310
  bucket.setResult(finishedStep, dataIndex, error, interfaces_js_1.FLAG_ERROR);
341
311
  }
342
312
  }
343
- if ((bucket.flagUnion & interfaces_js_1.FLAG_ERROR) === interfaces_js_1.FLAG_ERROR &&
344
- sideEffectSteps) {
345
- handleSideEffectSteps();
346
- }
347
313
  return promises ? Promise.all(promises) : undefined;
348
314
  })
349
315
  .then(null, (e) => {
@@ -359,9 +325,6 @@ function executeBucket(bucket, requestContext) {
359
325
  });
360
326
  }
361
327
  else {
362
- if ((bucket.flagUnion & interfaces_js_1.FLAG_ERROR) === interfaces_js_1.FLAG_ERROR && sideEffectSteps) {
363
- handleSideEffectSteps();
364
- }
365
328
  return promises ? Promise.all(promises) : undefined;
366
329
  }
367
330
  };
@@ -392,18 +355,30 @@ function executeBucket(bucket, requestContext) {
392
355
  bucket.store.set(step.id, batchExecutionValue((0, utils_js_1.arrayOfLength)(size)));
393
356
  }
394
357
  }
395
- outerLoop: for (let dataIndex = 0; dataIndex < size; dataIndex++) {
396
- if (sideEffectStepsWithErrors) {
397
- const currentPolymorphicPath = bucket.polymorphicPathList[dataIndex];
398
- for (const dep of sideEffectStepsWithErrors[currentPolymorphicPath ?? NO_POLY_PATH]) {
399
- const depExecutionValue = bucket.store.get(dep.id);
400
- const depFlags = depExecutionValue._flagsAt(dataIndex);
401
- if ((depFlags & interfaces_js_1.FLAG_POLY_SKIPPED) === interfaces_js_1.FLAG_POLY_SKIPPED) {
402
- /* noop */
403
- }
404
- else if ((depFlags & interfaces_js_1.FLAG_ERROR) === interfaces_js_1.FLAG_ERROR) {
405
- for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
406
- const step = _allSteps[allStepsIndex];
358
+ for (let dataIndex = 0; dataIndex < size; dataIndex++) {
359
+ stepLoop: for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
360
+ const step = (0, utils_js_1.sudo)(_allSteps[allStepsIndex]);
361
+ // Unary steps only need to be processed once
362
+ if (step._isUnary && dataIndex !== 0) {
363
+ continue;
364
+ }
365
+ // Check if the side effect errored
366
+ const $sideEffect = step.implicitSideEffectStep;
367
+ if ($sideEffect) {
368
+ let currentPolymorphicPath;
369
+ if ($sideEffect.polymorphicPaths === null ||
370
+ (currentPolymorphicPath =
371
+ bucket.polymorphicPathList[dataIndex]) === null ||
372
+ $sideEffect.polymorphicPaths.has(currentPolymorphicPath)) {
373
+ const depExecutionValue = bucket.store.get($sideEffect.id);
374
+ if (depExecutionValue === undefined) {
375
+ throw new Error(`GrafastInternalError<fcc8d302-ac66-40e8-aaea-ee2c7e2b30b2>: failed to get result for side effect ${$sideEffect} which impacts ${step}`);
376
+ }
377
+ const depFlags = depExecutionValue._flagsAt(dataIndex);
378
+ if (depFlags & interfaces_js_1.FLAG_POLY_SKIPPED) {
379
+ /* noop */
380
+ }
381
+ else if (depFlags & interfaces_js_1.FLAG_ERROR) {
407
382
  if (step._isUnary) {
408
383
  // COPY the unary value
409
384
  bucket.store.set(step.id, depExecutionValue);
@@ -413,17 +388,10 @@ function executeBucket(bucket, requestContext) {
413
388
  const depVal = depExecutionValue.at(dataIndex);
414
389
  bucket.setResult(step, dataIndex, depVal, depFlags);
415
390
  }
391
+ continue stepLoop;
416
392
  }
417
- continue outerLoop;
418
393
  }
419
394
  }
420
- }
421
- stepLoop: for (let allStepsIndex = executedLength; allStepsIndex < allStepsLength; allStepsIndex++) {
422
- const step = (0, utils_js_1.sudo)(_allSteps[allStepsIndex]);
423
- // Unary steps only need to be processed once
424
- if (step._isUnary && dataIndex !== 0) {
425
- continue;
426
- }
427
395
  try {
428
396
  const deps = [];
429
397
  const extra = extras[allStepsIndex];
@@ -630,7 +598,7 @@ function executeBucket(bucket, requestContext) {
630
598
  let newSize = 0;
631
599
  const stepPolymorphicPaths = step.polymorphicPaths;
632
600
  const legitDepsCount = (0, utils_js_1.sudo)(step).dependencies.length;
633
- let dependencies = sideEffectStepsWithErrors
601
+ let dependencies = step.implicitSideEffectStep
634
602
  ? dependenciesIncludingSideEffects.slice(0, legitDepsCount)
635
603
  : dependenciesIncludingSideEffects;
636
604
  // OPTIM: if unariesIncludingSideEffects.some(isGrafastError) then shortcut execution because everything fails
@@ -762,8 +730,11 @@ function executeBucket(bucket, requestContext) {
762
730
  const dependencies = _rawDependencies;
763
731
  let needsFiltering = false;
764
732
  const defaultForbiddenFlags = (0, utils_js_1.sudo)(step).defaultForbiddenFlags;
765
- const addDependency = (step, forbiddenFlags, onReject) => {
766
- const executionValue = store.get(step.id);
733
+ const addDependency = ($dep, forbiddenFlags, onReject) => {
734
+ const executionValue = store.get($dep.id);
735
+ if (executionValue === undefined) {
736
+ throw new Error(`GrafastInternalError<d9e9eb37-4251-4659-a545-4730826ecf0e>: ${$dep} data couldn't be found, but required by ${step} (with side effect ${step.implicitSideEffectStep})!`);
737
+ }
767
738
  _rawDependencies.push(executionValue);
768
739
  _rawForbiddenFlags.push(forbiddenFlags);
769
740
  _rawOnReject.push(onReject);
@@ -774,27 +745,15 @@ function executeBucket(bucket, requestContext) {
774
745
  };
775
746
  const sstep = (0, utils_js_1.sudo)(step);
776
747
  const depCount = sstep.dependencies.length;
777
- if (depCount > 0 || sideEffectStepsWithErrors !== null) {
748
+ if (depCount > 0) {
778
749
  for (let i = 0, l = depCount; i < l; i++) {
779
750
  const $dep = sstep.dependencies[i];
780
751
  addDependency($dep, sstep.dependencyForbiddenFlags[i], sstep.dependencyOnReject[i]);
781
752
  }
782
- if (sideEffectStepsWithErrors !== null) {
783
- if (sstep.polymorphicPaths) {
784
- for (const path of sstep.polymorphicPaths) {
785
- for (const sideEffectStep of sideEffectStepsWithErrors[path]) {
786
- // TODO: revisit this, feels like we might be adding the same
787
- // effect multiple times if it matches multiple paths.
788
- addDependency(sideEffectStep, defaultForbiddenFlags, undefined);
789
- }
790
- }
791
- }
792
- else {
793
- for (const sideEffectStep of sideEffectStepsWithErrors[NO_POLY_PATH]) {
794
- addDependency(sideEffectStep, defaultForbiddenFlags, undefined);
795
- }
796
- }
797
- }
753
+ }
754
+ const $sideEffect = step.implicitSideEffectStep;
755
+ if ($sideEffect) {
756
+ addDependency($sideEffect, defaultForbiddenFlags, undefined);
798
757
  }
799
758
  if (dev_js_1.isDev &&
800
759
  step.layerPlan.reason.type === "polymorphic" &&
package/dist/error.d.ts CHANGED
@@ -1,4 +1,16 @@
1
+ import type { ExecutionEntryFlags } from "./interfaces.js";
1
2
  import { $$safeError } from "./interfaces.js";
3
+ declare const $$flagged: unique symbol;
4
+ /**
5
+ * Wrapper for errors to return (rather than throw or reject) from user code.
6
+ */
7
+ export interface FlaggedValue<TValue = any> {
8
+ [$$flagged]: true;
9
+ flags: ExecutionEntryFlags;
10
+ value: TValue;
11
+ planId: number | null;
12
+ toString(): string;
13
+ }
2
14
  export declare const $$inhibit: FlaggedValue<null>;
3
15
  /**
4
16
  * Used to wrap error values to have Grafast treat them as if they were
@@ -15,4 +27,5 @@ export declare class SafeError<TExtensions extends Record<string, any> | undefin
15
27
  constructor(message: string, extensions?: TExtensions, errorOptions?: ErrorOptions);
16
28
  }
17
29
  export declare function isSafeError(error: Error): error is SafeError;
30
+ export {};
18
31
  //# sourceMappingURL=error.d.ts.map
@@ -1,15 +1,15 @@
1
1
  import type { AsyncExecutionResult, ExecutionResult } from "graphql";
2
2
  import type { PromiseOrValue } from "graphql/jsutils/PromiseOrValue";
3
3
  import type { GrafastArgs } from "./interfaces.js";
4
- /**
5
- * @deprecated Second and third parameters should be passed as part of args,
6
- * specifically `resolvedPreset` and `requestContext`.
7
- */
8
- export declare function grafast(args: GrafastArgs, legacyResolvedPreset?: GraphileConfig.ResolvedPreset, legacyCtx?: Partial<Grafast.RequestContext>): PromiseOrValue<ExecutionResult | AsyncGenerator<AsyncExecutionResult, void, undefined>>;
9
4
  /**
10
5
  * A replacement for GraphQL.js' `graphql` method that calls Grafast's
11
6
  * execute instead
12
7
  */
13
8
  export declare function grafast(args: GrafastArgs): PromiseOrValue<ExecutionResult | AsyncGenerator<AsyncExecutionResult, void, undefined>>;
9
+ /**
10
+ * @deprecated Second and third parameters should be passed as part of args,
11
+ * specifically `resolvedPreset` and `requestContext`.
12
+ */
13
+ export declare function grafast(args: GrafastArgs, legacyResolvedPreset?: GraphileConfig.ResolvedPreset | undefined, legacyCtx?: Partial<Grafast.RequestContext> | undefined): PromiseOrValue<ExecutionResult | AsyncGenerator<AsyncExecutionResult, void, undefined>>;
14
14
  export declare function grafastSync(args: GrafastArgs, legacyResolvedPreset?: GraphileConfig.ResolvedPreset, legacyRequestContext?: Partial<Grafast.RequestContext>): ExecutionResult;
15
15
  //# sourceMappingURL=grafastGraphql.d.ts.map
@@ -1,4 +1,10 @@
1
1
  import type { DirectiveNode } from "graphql";
2
2
  import type { __TrackedValueStep } from "./steps/index.js";
3
3
  export declare function evalDirectiveArgDirect<T = unknown>(directive: DirectiveNode, argumentName: string, variableValuesStep: __TrackedValueStep, defaultValue: T): T | undefined;
4
+ export declare function newSelectionSetDigest(resolverEmulation: boolean): {
5
+ label: undefined;
6
+ fields: Map<any, any>;
7
+ deferred: undefined;
8
+ resolverEmulation: boolean;
9
+ };
4
10
  //# sourceMappingURL=graphqlCollectFields.d.ts.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.graphqlCollectFields = exports.evalDirectiveArgDirect = exports.evalDirectiveArg = void 0;
3
+ exports.graphqlCollectFields = exports.newSelectionSetDigest = exports.evalDirectiveArgDirect = exports.evalDirectiveArg = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const graphql = tslib_1.__importStar(require("graphql"));
6
6
  const error_js_1 = require("./error.js");
@@ -100,6 +100,7 @@ const processFragment = (operationPlan, parentStepId, objectType, isMutation, se
100
100
  label,
101
101
  fields: new Map(),
102
102
  deferred: undefined,
103
+ resolverEmulation: selectionSetDigest.resolverEmulation,
103
104
  }
104
105
  : null;
105
106
  if (deferredDigest !== null) {
@@ -110,8 +111,17 @@ const processFragment = (operationPlan, parentStepId, objectType, isMutation, se
110
111
  selectionSetDigest.deferred.push(deferredDigest);
111
112
  }
112
113
  }
113
- graphqlCollectFields(operationPlan, parentStepId, objectType, fragmentSelectionSet.selections, isMutation, visitedFragments, deferredDigest ?? selectionSetDigest);
114
+ graphqlCollectFields(operationPlan, parentStepId, objectType, fragmentSelectionSet.selections, deferredDigest ?? selectionSetDigest, isMutation, visitedFragments);
114
115
  };
116
+ function newSelectionSetDigest(resolverEmulation) {
117
+ return {
118
+ label: undefined,
119
+ fields: new Map(),
120
+ deferred: undefined,
121
+ resolverEmulation,
122
+ };
123
+ }
124
+ exports.newSelectionSetDigest = newSelectionSetDigest;
115
125
  /**
116
126
  * Implements the `GraphQLCollectFields` algorithm - like `CollectFields` the
117
127
  * GraphQL spec, but modified such that access to variables is tracked.
@@ -120,13 +130,9 @@ const processFragment = (operationPlan, parentStepId, objectType, isMutation, se
120
130
  *
121
131
  * @internal
122
132
  */
123
- function graphqlCollectFields(operationPlan, parentStepId, objectType, selections, isMutation = false,
133
+ function graphqlCollectFields(operationPlan, parentStepId, objectType, selections, selectionSetDigest, isMutation = false,
124
134
  // This is significantly faster than an array or a Set
125
- visitedFragments = Object.create(null), selectionSetDigest = {
126
- label: undefined,
127
- fields: new Map(),
128
- deferred: undefined,
129
- }) {
135
+ visitedFragments = Object.create(null)) {
130
136
  // const objectTypeFields = objectType.getFields();
131
137
  const trackedVariableValuesStep = operationPlan.trackedVariableValuesStep;
132
138
  for (let i = 0, l = selections.length; i < l; i++) {
@@ -83,6 +83,10 @@ export declare const $$data: unique symbol;
83
83
  * example details of the plan or SQL queries or similar that were executed.
84
84
  */
85
85
  export declare const $$extensions: unique symbol;
86
+ /**
87
+ * The "GraphQLObjectType" type name, useful when dealing with polymorphism.
88
+ */
89
+ export declare const $$concreteType: unique symbol;
86
90
  /**
87
91
  * Set this key on a type if that type's serialization is idempotent (that is
88
92
  * to say `serialize(serialize(thing)) === serialize(thing)`). This means we
@@ -399,6 +403,20 @@ export interface ExecutionExtra extends ExecutionExtraBase {
399
403
  }
400
404
  export interface UnbatchedExecutionExtra extends ExecutionExtraBase {
401
405
  }
406
+ /**
407
+ * A bitwise number representing a number of flags:
408
+ *
409
+ * - 0: normal execution value
410
+ * - 1: errored (trappable)
411
+ * - 2: null (trappable)
412
+ * - 4: inhibited (trappable)
413
+ * - 8: disabled due to polymorphism (untrappable)
414
+ * - 16: stopped (untrappable) - e.g. due to fatal (unrecoverable) error
415
+ * - 32: ...
416
+ */
417
+ export type ExecutionEntryFlags = number & {
418
+ readonly tsBrand?: unique symbol;
419
+ };
402
420
  export declare const NO_FLAGS: ExecutionEntryFlags;
403
421
  export declare const FLAG_ERROR: ExecutionEntryFlags;
404
422
  export declare const FLAG_NULL: ExecutionEntryFlags;
@@ -28,8 +28,6 @@ exports.$$data = Symbol("data");
28
28
  exports.$$extensions = Symbol("extensions");
29
29
  /**
30
30
  * The "GraphQLObjectType" type name, useful when dealing with polymorphism.
31
- *
32
- * @internal
33
31
  */
34
32
  exports.$$concreteType = Symbol("concreteType");
35
33
  /**
package/dist/mermaid.js CHANGED
@@ -137,7 +137,7 @@ concise = false, skipBuckets = global.grafastExplainMermaidSkipBuckets ?? false,
137
137
  pp(stepById[plan.dependencyIds[0]].polymorphicPaths) === polyPaths
138
138
  ? ""
139
139
  : `\n${polyPaths}`;
140
- const planString = `${planName}[${plan.id}${`∈${plan.bucketId}`}]${meta ? `\n<${meta}>` : ""}${polyPathsIfDifferent}`;
140
+ const planString = `${planName}[${plan.id}${`∈${plan.bucketId}`}]${plan.isUnary ? " ➊" : ""}${meta ? `\n<${meta}>` : ""}${polyPathsIfDifferent}`;
141
141
  const [lBrace, rBrace] = plan.stepClass === "__ItemStep"
142
142
  ? ["[/", "\\]"]
143
143
  : plan.isSyncAndSafe
@@ -267,6 +267,18 @@ concise = false, skipBuckets = global.grafastExplainMermaidSkipBuckets ?? false,
267
267
  const stepToString = (step) => {
268
268
  return `${step.stepClass.replace(/Step$/, "")}${step.bucketId === 0 ? "" : `{${step.bucketId}}`}${step.metaString ? `<${step.metaString}>` : ""}[${step.id}]`;
269
269
  };
270
+ let firstSideEffect = true;
271
+ sortedSteps.forEach((step) => {
272
+ if (step.implicitSideEffectStepId) {
273
+ if (firstSideEffect) {
274
+ graph.push("");
275
+ graph.push(" %% implicit side effects");
276
+ firstSideEffect = false;
277
+ }
278
+ const sideEffectStep = stepById[step.implicitSideEffectStepId];
279
+ graph.push(` ${planId(sideEffectStep)} -.-o ${planId(step)}`);
280
+ }
281
+ });
270
282
  graph.push("");
271
283
  if (!concise && !skipBuckets)
272
284
  graph.push(" subgraph Buckets");
@@ -278,17 +290,18 @@ concise = false, skipBuckets = global.grafastExplainMermaidSkipBuckets ?? false,
278
290
  (layerPlan.reason.type === "polymorphic"
279
291
  ? `\n${layerPlan.reason.typeNames}`
280
292
  : ``);
281
- const outputMapStuff = [];
282
293
  if (!skipBuckets) {
283
- graph.push(` Bucket${layerPlan.id}(${(0, exports.mermaidEscape)(`Bucket ${layerPlan.id}${raisonDEtre}${layerPlan.copyStepIds.length > 0
294
+ graph.push(` Bucket${layerPlan.id}(${(0, exports.mermaidEscape)(`Bucket ${layerPlan.id}${raisonDEtre}${layerPlan.parentSideEffectStepId != null
295
+ ? `\nParent side effect step: ${stepById[layerPlan.parentSideEffectStepId].id}`
296
+ : ""}${layerPlan.copyStepIds.length > 0
284
297
  ? `\nDeps: ${layerPlan.copyStepIds
285
298
  .map((pId) => stepById[pId].id)
286
- .join(", ")}\n`
299
+ .join(", ")}`
287
300
  : ""}${layerPlan.reason.type === "polymorphic"
288
- ? pp(layerPlan.reason.polymorphicPaths)
289
- : ""}${layerPlan.rootStepId != null && layerPlan.reason.type !== "root"
301
+ ? "\n" + pp(layerPlan.reason.polymorphicPaths)
302
+ : ""}\n${layerPlan.rootStepId != null && layerPlan.reason.type !== "root"
290
303
  ? `\nROOT ${stepToString(stepById[layerPlan.rootStepId])}`
291
- : ""}${startSteps(layerPlan)}\n${outputMapStuff.join("\n")}`)}):::bucket`);
304
+ : ""}${startSteps(layerPlan)}`)}):::bucket`);
292
305
  }
293
306
  graph.push(` classDef bucket${layerPlan.id} stroke:${color(layerPlan.id)}`);
294
307
  graph.push(` class ${[`Bucket${layerPlan.id}`, ...steps.map(planId)].join(",")} bucket${layerPlan.id}`);
@@ -302,10 +315,6 @@ concise = false, skipBuckets = global.grafastExplainMermaidSkipBuckets ?? false,
302
315
  }
303
316
  }
304
317
  }
305
- const isUnary = (step) => step.isUnary;
306
- graph.push(`\
307
- classDef unary fill:#fafffa,borderWidth:8px
308
- class ${sortedSteps.filter(isUnary).map(planId)} unary`);
309
318
  if (!concise && !skipBuckets)
310
319
  graph.push(" end");
311
320
  const graphString = graph.join("\n");
@@ -310,7 +310,14 @@ function withFieldArgsForArguments(operationPlan, $parent, $all, field, applyAft
310
310
  if (mode === "apply" &&
311
311
  (!subpath || (Array.isArray(subpath) && subpath.length === 0))) {
312
312
  if (isInputObjectType(nullableEntityType)) {
313
- for (const fieldName of Object.keys(nullableEntityType.getFields())) {
313
+ if (!("evalKeys" in $input)) {
314
+ throw new Error(`GrafastInternalError<d782160e-7a99-4a12-b690-435c1ab7b676>: Expected ${$input} to be a __InputObjectStep or __TrackedValueStep (i.e. to have 'evalKeys')`);
315
+ }
316
+ const keys = $input.evalKeys();
317
+ if (keys == null) {
318
+ return;
319
+ }
320
+ for (const fieldName of keys) {
314
321
  const $target = typeof targetStepOrCallback === "function"
315
322
  ? targetStepOrCallback()
316
323
  : targetStepOrCallback;
@@ -8,6 +8,7 @@ export interface GrafastPlanStepJSONv1 {
8
8
  metaString: string | null;
9
9
  isUnary: boolean;
10
10
  bucketId: string | number;
11
+ implicitSideEffectStepId: string | number | null;
11
12
  dependencyIds: ReadonlyArray<string | number>;
12
13
  dependencyForbiddenFlags: ReadonlyArray<ExecutionEntryFlags>;
13
14
  dependencyOnReject: ReadonlyArray<string | null | undefined>;
@@ -69,6 +70,7 @@ export interface GrafastPlanBucketReasonSubroutineJSONv1 {
69
70
  export interface GrafastPlanBucketJSONv1 {
70
71
  id: number;
71
72
  reason: GrafastPlanBucketReasonJSONv1;
73
+ parentSideEffectStepId: string | number | null;
72
74
  copyStepIds: ReadonlyArray<string | number>;
73
75
  steps: ReadonlyArray<GrafastPlanStepJSONv1>;
74
76
  rootStepId: string | number | null;
package/dist/prepare.js CHANGED
@@ -152,7 +152,7 @@ function outputBucket(outputPlan, rootBucket, rootBucketIndex, requestContext, p
152
152
  childBucketIndex = rootBucketIndex;
153
153
  }
154
154
  else {
155
- const c = (0, OutputPlan_js_1.getChildBucketAndIndex)(outputPlan, null, rootBucket, rootBucketIndex, null);
155
+ const c = (0, OutputPlan_js_1.getChildBucketAndIndex)(outputPlan.layerPlan, null, rootBucket, rootBucketIndex, null);
156
156
  if (!c) {
157
157
  throw new Error(`GrafastInternalError<8bbf56c1-8e2a-4ee9-b5fc-724fd0ee222b>: could not find relevant bucket for output plan`);
158
158
  }
@@ -250,11 +250,11 @@ function executePreemptive(args, operationPlan, variableValues, context, rootVal
250
250
  releaseUnusedIterators(rootBucket, rootBucketIndex, null);
251
251
  // Something major went wrong!
252
252
  const errors = [
253
- new GraphQLError(bucketRootValue.originalError.message, operationPlan.rootOutputPlan.locationDetails.node, // node
253
+ new GraphQLError(bucketRootValue.message, operationPlan.rootOutputPlan.locationDetails.node, // node
254
254
  undefined, // source
255
255
  null, // positions
256
256
  null, // path
257
- bucketRootValue.originalError, // originalError
257
+ bucketRootValue, // originalError
258
258
  null),
259
259
  ];
260
260
  const payload = Object.create(null);
package/dist/step.d.ts CHANGED
@@ -27,11 +27,6 @@ export declare abstract class BaseStep {
27
27
  isArgumentsFinalized: boolean;
28
28
  isFinalized: boolean;
29
29
  debug: boolean;
30
- /**
31
- * Set this true for plans that implement mutations; this will prevent them
32
- * from being tree-shaken.
33
- */
34
- hasSideEffects: boolean;
35
30
  constructor();
36
31
  toString(): string;
37
32
  /**
@@ -113,6 +108,11 @@ export declare class ExecutableStep<TData = any> extends BaseStep {
113
108
  * Like `metaKey` but for the optimize phase
114
109
  */
115
110
  optimizeMetaKey: number | string | symbol | undefined;
111
+ /**
112
+ * Set this true for plans that implement mutations; this will prevent them
113
+ * from being tree-shaken.
114
+ */
115
+ hasSideEffects: boolean;
116
116
  constructor();
117
117
  protected withMyLayerPlan<T>(callback: () => T): T;
118
118
  protected getStep(id: number): ExecutableStep;
package/dist/step.js CHANGED
@@ -63,7 +63,6 @@ class BaseStep {
63
63
  this.isArgumentsFinalized = false;
64
64
  this.isFinalized = false;
65
65
  this.debug = (0, global_js_1.getDebug)();
66
- this.hasSideEffects = false;
67
66
  const layerPlan = (0, withGlobalLayerPlan_js_1.currentLayerPlan)();
68
67
  this.layerPlan = layerPlan;
69
68
  this.operationPlan = layerPlan.operationPlan;
@@ -108,6 +107,57 @@ class ExecutableStep extends BaseStep {
108
107
  * (default = ALL_FLAGS & ~FLAG_NULL)
109
108
  */
110
109
  this.defaultForbiddenFlags = interfaces_js_1.DEFAULT_FORBIDDEN_FLAGS;
110
+ this.implicitSideEffectStep = null;
111
+ this.hasSideEffects ??= false;
112
+ let hasSideEffects = false;
113
+ const stepTracker = this.layerPlan.operationPlan.stepTracker;
114
+ Object.defineProperty(this, "hasSideEffects", {
115
+ get() {
116
+ return hasSideEffects;
117
+ },
118
+ set(value) {
119
+ /**
120
+ * If steps were created after this step, an this step doesn't depend
121
+ * on them, then it's no longer safe to change hasSideEffects.
122
+ */
123
+ let nonDependentSteps = null;
124
+ const maxStepId = stepTracker.stepCount - 1;
125
+ if (this.id === maxStepId) {
126
+ // All good - no more steps were created
127
+ }
128
+ else {
129
+ // If the step created them during initialization and is dependent on
130
+ // them, that's fine too.
131
+ for (let id = this.id + 1; id <= maxStepId; id++) {
132
+ const step = stepTracker.getStepById(id);
133
+ if ((0, utils_js_1.stepADependsOnStepB)(this, step))
134
+ continue;
135
+ if (nonDependentSteps === null) {
136
+ nonDependentSteps = [step];
137
+ }
138
+ else {
139
+ nonDependentSteps.push(step);
140
+ }
141
+ }
142
+ }
143
+ if (nonDependentSteps === null) {
144
+ hasSideEffects = value;
145
+ if (value === true) {
146
+ this.layerPlan.latestSideEffectStep = this;
147
+ }
148
+ else if (value !== true && hasSideEffects === true) {
149
+ throw new Error(`Cannot mark ${this} as having no side effects after having set it to have side effects.`);
150
+ }
151
+ }
152
+ else {
153
+ throw new Error(`Attempted to mark ${this} as having side effects, but other non-dependent steps (${nonDependentSteps
154
+ .map(String)
155
+ .join(", ")}) have already been created.`);
156
+ }
157
+ },
158
+ enumerable: true,
159
+ configurable: false,
160
+ });
111
161
  this.dependencies = [];
112
162
  this.dependencyForbiddenFlags = [];
113
163
  this.dependencyOnReject = [];
@@ -117,6 +167,7 @@ class ExecutableStep extends BaseStep {
117
167
  this._stepOptions = { stream: null };
118
168
  this.store = true;
119
169
  this.polymorphicPaths = (0, withGlobalLayerPlan_js_1.currentPolymorphicPaths)();
170
+ // Important: MUST come after `this.layerPlan = ...`
120
171
  this.id = this.layerPlan._addStep(this);
121
172
  }
122
173
  withMyLayerPlan(callback) {
@@ -163,7 +214,7 @@ class ExecutableStep extends BaseStep {
163
214
  catch (e) {
164
215
  // Ignore
165
216
  }
166
- return chalk_1.default.bold.blue(`${this.constructor.name.replace(/Step$/, "")}${this.layerPlan.id === 0 ? "" : chalk_1.default.grey(`{${this.layerPlan.id}}`)}${meta != null && meta.length ? chalk_1.default.grey(`<${meta}>`) : ""}[${(0, inspect_js_1.inspect)(this.id, {
217
+ return chalk_1.default.bold.blue(`${this.constructor.name.replace(/Step$/, "")}${this.layerPlan.id === 0 ? "" : chalk_1.default.grey(`{${this.layerPlan.id}}`)}${this._isUnary ? "➊" : ""}${meta != null && meta.length ? chalk_1.default.grey(`<${meta}>`) : ""}[${(0, inspect_js_1.inspect)(this.id, {
167
218
  colors: true,
168
219
  })}]`);
169
220
  }
@@ -1,5 +1,6 @@
1
1
  import type { ExecutionDetails, ExecutionEntryFlags, GrafastResultsList } from "../interfaces.js";
2
2
  import { $$deepDepSkip } from "../interfaces.js";
3
+ import type { ListCapableStep } from "../step.js";
3
4
  import { ExecutableStep } from "../step.js";
4
5
  import type { __ItemStep } from "./__item.js";
5
6
  export declare const TRAP_ERROR: ExecutionEntryFlags;
@@ -32,8 +33,8 @@ export declare class __FlagStep<TData> extends ExecutableStep<TData> {
32
33
  constructor(step: ExecutableStep, options: FlagStepOptions);
33
34
  toStringMeta(): string | null;
34
35
  [$$deepDepSkip](): ExecutableStep;
35
- listItem?: ($item: __ItemStep<this>) => ExecutableStep;
36
- _listItem($item: __ItemStep<this>): ExecutableStep<unknown>;
36
+ listItem?: ($item: __ItemStep<ListCapableStep<unknown, ExecutableStep<unknown>>>) => ExecutableStep;
37
+ _listItem($item: __ItemStep<ListCapableStep<unknown, ExecutableStep<unknown>>>): ExecutableStep<unknown>;
37
38
  deduplicate(_peers: readonly ExecutableStep<any>[]): readonly ExecutableStep<any>[];
38
39
  execute(_details: ExecutionDetails<[data: TData, cond?: boolean]>): GrafastResultsList<TData>;
39
40
  finalize(): void;
@@ -22,6 +22,7 @@ export declare class __InputObjectStep<TInputType extends GraphQLInputObjectType
22
22
  evalIs(value: null | undefined | 0): boolean;
23
23
  evalIsEmpty(): boolean;
24
24
  evalHas(attrName: string): boolean;
25
+ evalKeys(): ReadonlyArray<keyof TInputType & string> | null;
25
26
  }
26
27
  export type __InputObjectStepWithDollars<TInputType extends GraphQLInputObjectType = GraphQLInputObjectType> = __InputObjectStep<TInputType> & {
27
28
  [key in keyof ReturnType<TInputType["getFields"]> & string as `$${key}`]: InputStep<ReturnType<TInputType["getFields"]>[key]["type"]>;