grafast 0.1.1-beta.19 → 0.1.1-beta.20

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.
@@ -1,4 +1,5 @@
1
1
  import type { FragmentDefinitionNode, GraphQLFieldMap, GraphQLObjectType, GraphQLSchema, GraphQLUnionType, OperationDefinitionNode } from "graphql";
2
+ import type { Constraint } from "../constraints.js";
2
3
  import type { SelectionSetDigest } from "../graphqlCollectFields.js";
3
4
  import type { GrafastPlanJSON } from "../index.js";
4
5
  import { ExecutableStep } from "../index.js";
@@ -69,11 +70,11 @@ export declare class OperationPlan {
69
70
  private scalarPlanInfo;
70
71
  constructor(schema: GraphQLSchema, operation: OperationDefinitionNode, fragments: {
71
72
  [fragmentName: string]: FragmentDefinitionNode;
72
- }, variableValues: {
73
+ }, variableValuesConstraints: Constraint[], variableValues: {
73
74
  [key: string]: any;
74
- }, context: {
75
+ }, contextConstraints: Constraint[], context: {
75
76
  [key: string]: any;
76
- }, rootValue: any, planningTimeout: number | null);
77
+ }, rootValueConstraints: Constraint[], rootValue: any, planningTimeout: number | null);
77
78
  private lap;
78
79
  private checkTimeout;
79
80
  private planOperation;
@@ -198,7 +199,7 @@ export declare class OperationPlan {
198
199
  *
199
200
  * @experimental
200
201
  */
201
- cacheStep<T extends ExecutableStep>(ownerStep: ExecutableStep, actionKey: string, cacheKey: symbol | string | number, cb: () => T): T;
202
+ cacheStep<T extends ExecutableStep>(ownerStep: ExecutableStep, actionKey: string, cacheKey: symbol | string | number | boolean | null | undefined, cb: () => T): T;
202
203
  /**
203
204
  * Clears the cache, typically due to side effects having taken place. Called
204
205
  * from setting hasSideEffects on an ExecutableStep, among other places.
@@ -62,7 +62,7 @@ const NO_ARGS = {
62
62
  },
63
63
  };
64
64
  class OperationPlan {
65
- constructor(schema, operation, fragments, variableValues, context, rootValue, planningTimeout) {
65
+ constructor(schema, operation, fragments, variableValuesConstraints, variableValues, contextConstraints, context, rootValueConstraints, rootValue, planningTimeout) {
66
66
  this.schema = schema;
67
67
  this.operation = operation;
68
68
  this.fragments = fragments;
@@ -96,12 +96,6 @@ class OperationPlan {
96
96
  this.stepTracker = new StepTracker_js_1.StepTracker(this);
97
97
  this.maxDeduplicatedStepId = -1;
98
98
  this.maxValidatedStepId = -1;
99
- /** Constraints based on evaluating variables. @internal */
100
- this.variableValuesConstraints = [];
101
- /** Constraints based on evaluating context. @internal */
102
- this.contextConstraints = [];
103
- /** Constraints based on evaluating rootValue. @internal */
104
- this.rootValueConstraints = [];
105
99
  /**
106
100
  * @internal
107
101
  */
@@ -138,6 +132,9 @@ class OperationPlan {
138
132
  };
139
133
  this.polymorphicLayerPlanByPathByLayerPlan = new Map();
140
134
  this._cacheStepStoreByLayerPlanAndActionKey = Object.create(null);
135
+ this.variableValuesConstraints = variableValuesConstraints;
136
+ this.contextConstraints = contextConstraints;
137
+ this.rootValueConstraints = rootValueConstraints;
141
138
  this.scalarPlanInfo = { schema: this.schema };
142
139
  const queryType = schema.getQueryType();
143
140
  assert.ok(queryType, "Schema must have a query type");
@@ -1412,6 +1409,16 @@ class OperationPlan {
1412
1409
  */
1413
1410
  getPeers(step) {
1414
1411
  if (step.hasSideEffects) {
1412
+ // Plans with side effects have no peers.
1413
+ return EMPTY_ARRAY;
1414
+ }
1415
+ if (step._stepOptions.stream) {
1416
+ // Streams have no peers - we cannot reference the stream more
1417
+ // than once (and we aim to not cache the stream because we want its
1418
+ // entries to be garbage collected).
1419
+ //
1420
+ // HOWEVER! There may be lifecycle parts that need to be called... So
1421
+ // call the function with an empty array; ignore the result.
1415
1422
  return EMPTY_ARRAY;
1416
1423
  }
1417
1424
  const { dependencies: deps, dependencyForbiddenFlags: flags, dependencyOnReject: onReject, layerPlan: layerPlan, constructor: stepConstructor, peerKey, } = (0, utils_js_1.sudo)(step);
@@ -1868,24 +1875,12 @@ class OperationPlan {
1868
1875
  return step;
1869
1876
  }
1870
1877
  _deduplicateInnerLogic(step) {
1871
- if (step.hasSideEffects) {
1872
- // Never deduplicate plans with side effects.
1873
- return null;
1874
- }
1875
1878
  if (step instanceof index_js_1.__ItemStep) {
1876
1879
  // __ItemStep cannot be deduplicated
1877
1880
  return null;
1878
1881
  }
1879
- // OPTIMIZE: Past Benjie thought we might want to revisit this under the
1880
- // new Grafast system. No idea what he had in mind there though...
1881
- if (step._stepOptions.stream) {
1882
- // Never deduplicate streaming plans, we cannot reference the stream more
1883
- // than once (and we aim to not cache the stream because we want its
1884
- // entries to be garbage collected).
1885
- return null;
1886
- }
1887
1882
  const peers = this.getPeers(step);
1888
- // Even if there is just one peer, we must still deduplicate because
1883
+ // Even if there are no peers, we must still deduplicate because
1889
1884
  // `deduplicate` is called to indicate that the field is done being
1890
1885
  // planned, which the step class may want to acknowledge by locking certain
1891
1886
  // facets of its functionality (such as adding filters). We'll simplify its
@@ -2633,19 +2628,19 @@ class OperationPlan {
2633
2628
  */
2634
2629
  cacheStep(ownerStep, actionKey, cacheKey, cb) {
2635
2630
  const layerPlan = (0, withGlobalLayerPlan_js_1.currentLayerPlan)();
2636
- const cache = (this._cacheStepStoreByLayerPlanAndActionKey[`${actionKey}|${layerPlan.id}|${ownerStep.id}`] ??= Object.create(null));
2631
+ const cache = (this._cacheStepStoreByLayerPlanAndActionKey[`${actionKey}|${layerPlan.id}|${ownerStep.id}`] ??= new Map());
2637
2632
  const cacheIt = () => {
2638
2633
  const stepToCache = cb();
2639
2634
  if (!(stepToCache instanceof index_js_1.ExecutableStep)) {
2640
2635
  throw new Error(`The callback passed to cacheStep must always return an ExecutableStep; but this call from ${ownerStep} returned instead ${(0, inspect_js_1.inspect)(stepToCache)}`);
2641
2636
  }
2642
- cache[cacheKey] = stepToCache.id;
2637
+ cache.set(cacheKey, stepToCache.id);
2643
2638
  return stepToCache;
2644
2639
  };
2645
- if (!(cacheKey in cache)) {
2640
+ if (!cache.has(cacheKey)) {
2646
2641
  return cacheIt();
2647
2642
  }
2648
- const cachedStepId = cache[cacheKey];
2643
+ const cachedStepId = cache.get(cacheKey);
2649
2644
  const cachedStep = this.stepTracker.stepById[cachedStepId];
2650
2645
  return cachedStep ?? cacheIt();
2651
2646
  }
@@ -50,7 +50,7 @@ const assertFragmentsMatch = !dev_js_1.isDev ? dev_js_1.noop : reallyAssertFragm
50
50
  * @remarks Due to the optimisation in `establishOperationPlan`, the schema, document
51
51
  * and operationName checks have already been performed.
52
52
  */
53
- function isOperationPlanCompatible(operationPlan, variableValues, context, rootValue) {
53
+ function isOperationPlanResultCompatible(operationPlan, variableValues, context, rootValue) {
54
54
  const { variableValuesConstraints, contextConstraints, rootValueConstraints, } = operationPlan;
55
55
  if (!(0, constraints_js_1.matchesConstraints)(variableValuesConstraints, variableValues)) {
56
56
  return false;
@@ -84,49 +84,54 @@ function establishOperationPlan(schema, operation, fragments, variableValues, co
84
84
  let linkedItem = cache.possibleOperationPlans;
85
85
  while (linkedItem) {
86
86
  const value = linkedItem.value;
87
- if (value instanceof index_js_1.SafeError) {
88
- if (value.extensions?.[interfaces_js_1.$$timeout] != null) {
89
- if (value.extensions[interfaces_js_1.$$ts] < timeSource_js_1.timeSource.now() - TIMEOUT_TIMEOUT) {
90
- // Remove this out of date timeout
91
- linkedItem = linkedItem.next;
92
- if (previousItem !== null) {
93
- previousItem.next = linkedItem;
87
+ if (isOperationPlanResultCompatible(value, variableValues, context, rootValue)) {
88
+ const { error, operationPlan } = value;
89
+ if (error != null) {
90
+ if (error instanceof index_js_1.SafeError) {
91
+ if (error.extensions?.[interfaces_js_1.$$timeout] != null) {
92
+ if (error.extensions[interfaces_js_1.$$ts] < timeSource_js_1.timeSource.now() - TIMEOUT_TIMEOUT) {
93
+ // Remove this out of date timeout
94
+ linkedItem = linkedItem.next;
95
+ if (previousItem !== null) {
96
+ previousItem.next = linkedItem;
97
+ }
98
+ else {
99
+ cache.possibleOperationPlans = linkedItem;
100
+ }
101
+ continue;
102
+ }
103
+ if (planningTimeout !== null &&
104
+ error.extensions[interfaces_js_1.$$timeout] >= planningTimeout) {
105
+ // It was a timeout error - do not retry
106
+ throw error;
107
+ }
108
+ else {
109
+ // That's Not My Timeout, let's try again.
110
+ }
94
111
  }
95
112
  else {
96
- cache.possibleOperationPlans = linkedItem;
113
+ // Not a timeout error - this will always fail in the same way?
114
+ throw error;
97
115
  }
98
- continue;
99
- }
100
- if (planningTimeout !== null &&
101
- value.extensions[interfaces_js_1.$$timeout] >= planningTimeout) {
102
- // It was a timeout error - do not retry
103
- throw value;
104
116
  }
105
117
  else {
106
- // That's Not My Timeout, let's try again.
118
+ // Not a timeout error - this will always fail in the same way?
119
+ throw error;
107
120
  }
108
121
  }
109
122
  else {
110
- // Not a timeout error - this will always fail in the same way?
111
- throw value;
112
- }
113
- }
114
- else if (value instanceof Error) {
115
- // Not a timeout error - this will always fail in the same way?
116
- throw value;
117
- }
118
- else if (isOperationPlanCompatible(value, variableValues, context, rootValue)) {
119
- // Hoist to top of linked list
120
- if (previousItem !== null) {
121
- // Remove linkedItem from existing chain
122
- previousItem.next = linkedItem.next;
123
- // Add rest of chain after linkedItem
124
- linkedItem.next = cache.possibleOperationPlans;
125
- // linkedItem is now head of chain
126
- cache.possibleOperationPlans = linkedItem;
123
+ // Hoist to top of linked list
124
+ if (previousItem !== null) {
125
+ // Remove linkedItem from existing chain
126
+ previousItem.next = linkedItem.next;
127
+ // Add rest of chain after linkedItem
128
+ linkedItem.next = cache.possibleOperationPlans;
129
+ // linkedItem is now head of chain
130
+ cache.possibleOperationPlans = linkedItem;
131
+ }
132
+ // We found a suitable OperationPlan - use that!
133
+ return operationPlan;
127
134
  }
128
- // We found a suitable OperationPlan - use that!
129
- return value;
130
135
  }
131
136
  count++;
132
137
  lastButOneItem = previousItem;
@@ -137,8 +142,11 @@ function establishOperationPlan(schema, operation, fragments, variableValues, co
137
142
  // No suitable OperationPlan found, time to make one.
138
143
  let operationPlan;
139
144
  let error;
145
+ const variableValuesConstraints = [];
146
+ const contextConstraints = [];
147
+ const rootValueConstraints = [];
140
148
  try {
141
- operationPlan = new index_js_1.OperationPlan(schema, operation, fragments, variableValues, context, rootValue, planningTimeout);
149
+ operationPlan = new index_js_1.OperationPlan(schema, operation, fragments, variableValuesConstraints, variableValues, contextConstraints, context, rootValueConstraints, rootValue, planningTimeout);
142
150
  }
143
151
  catch (e) {
144
152
  error = e;
@@ -153,11 +161,19 @@ function establishOperationPlan(schema, operation, fragments, variableValues, co
153
161
  });
154
162
  schema.extensions.grafast[interfaces_js_1.$$cacheByOperation] = cacheByOperation;
155
163
  }
156
- const operationPlanOrError = operationPlan ?? error;
164
+ const establishOperationPlanResult = {
165
+ variableValuesConstraints,
166
+ contextConstraints,
167
+ rootValueConstraints,
168
+ ...(operationPlan ? { operationPlan } : { error: error }),
169
+ };
157
170
  if (!cache) {
158
171
  cache = {
159
172
  fragments,
160
- possibleOperationPlans: { value: operationPlanOrError, next: null },
173
+ possibleOperationPlans: {
174
+ value: establishOperationPlanResult,
175
+ next: null,
176
+ },
161
177
  };
162
178
  cacheByOperation.set(operation, cache);
163
179
  }
@@ -171,7 +187,7 @@ function establishOperationPlan(schema, operation, fragments, variableValues, co
171
187
  }
172
188
  // Add new operationPlan to top of the linked list.
173
189
  cache.possibleOperationPlans = {
174
- value: operationPlanOrError,
190
+ value: establishOperationPlanResult,
175
191
  next: cache.possibleOperationPlans,
176
192
  };
177
193
  }
package/dist/inspect.js CHANGED
@@ -13,7 +13,7 @@ catch {
13
13
  !obj ||
14
14
  Object.getPrototypeOf(obj) === null ||
15
15
  Object.getPrototypeOf(obj) === Object.prototype
16
- ? JSON.stringify(obj)
16
+ ? String(JSON.stringify(obj))
17
17
  : String(obj);
18
18
  };
19
19
  }
@@ -2,6 +2,7 @@ import type EventEmitter from "eventemitter3";
2
2
  import type { Middleware } from "graphile-config";
3
3
  import type { ASTNode, ExecutionArgs, FragmentDefinitionNode, GraphQLArgs, GraphQLArgument, GraphQLArgumentConfig, GraphQLField, GraphQLFieldConfig, GraphQLInputField, GraphQLInputFieldConfig, GraphQLInputObjectType, GraphQLInputType, GraphQLList, GraphQLNonNull, GraphQLOutputType, GraphQLScalarType, GraphQLSchema, GraphQLType, OperationDefinitionNode, Source, ValueNode, VariableNode } from "graphql";
4
4
  import type { ObjMap } from "graphql/jsutils/ObjMap.js";
5
+ import type { Constraint } from "./constraints.js";
5
6
  import type { OperationPlan } from "./engine/OperationPlan.js";
6
7
  import type { FlaggedValue, SafeError } from "./error.js";
7
8
  import type { ExecutableStep, ListCapableStep, ModifierStep, StreamableStep } from "./step.js";
@@ -38,13 +39,26 @@ export declare const $$cacheByOperation: unique symbol;
38
39
  export type Fragments = {
39
40
  [key: string]: FragmentDefinitionNode;
40
41
  };
41
- export type OperationPlanOrError = OperationPlan | Error | SafeError<{
42
- [$$timeout]: number;
43
- [$$ts]: number;
44
- } | {
45
- [$$timeout]?: undefined;
46
- [$$ts]?: undefined;
47
- } | undefined>;
42
+ export interface IEstablishOperationPlanResult {
43
+ variableValuesConstraints: Constraint[];
44
+ contextConstraints: Constraint[];
45
+ rootValueConstraints: Constraint[];
46
+ }
47
+ export interface EstablishOperationPlanResultSuccess extends IEstablishOperationPlanResult {
48
+ error?: never;
49
+ operationPlan: OperationPlan;
50
+ }
51
+ export interface EstablishOperationPlanResultError extends IEstablishOperationPlanResult {
52
+ error: Error | SafeError<{
53
+ [$$timeout]: number;
54
+ [$$ts]: number;
55
+ } | {
56
+ [$$timeout]?: undefined;
57
+ [$$ts]?: undefined;
58
+ } | undefined>;
59
+ operationPlan?: never;
60
+ }
61
+ export type EstablishOperationPlanResult = EstablishOperationPlanResultSuccess | EstablishOperationPlanResultError;
48
62
  /**
49
63
  * This represents the list of possible operationPlans for a specific document.
50
64
  *
@@ -59,7 +73,7 @@ export interface CacheByOperationEntry {
59
73
  * list, and if the list grows beyond a maximum size we can drop the last
60
74
  * element.
61
75
  */
62
- possibleOperationPlans: LinkedList<OperationPlanOrError> | null;
76
+ possibleOperationPlans: LinkedList<EstablishOperationPlanResult> | null;
63
77
  fragments: Fragments;
64
78
  }
65
79
  export interface LinkedList<T> {
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const version = "0.1.1-beta.19";
1
+ export declare const version = "0.1.1-beta.20";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
4
  // This file is autogenerated by /scripts/postversion.mjs
5
- exports.version = "0.1.1-beta.19";
5
+ exports.version = "0.1.1-beta.20";
6
6
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grafast",
3
- "version": "0.1.1-beta.19",
3
+ "version": "0.1.1-beta.20",
4
4
  "description": "Cutting edge GraphQL planning and execution engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",