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.
- package/README.md +0 -3
- package/dist/args.js +8 -2
- package/dist/constraints.d.ts +15 -1
- package/dist/constraints.js +40 -1
- package/dist/engine/LayerPlan.d.ts +1 -0
- package/dist/engine/LayerPlan.js +62 -9
- package/dist/engine/OperationPlan.js +372 -274
- package/dist/engine/OutputPlan.d.ts +6 -5
- package/dist/engine/OutputPlan.js +423 -444
- package/dist/engine/executeBucket.js +36 -77
- package/dist/error.d.ts +13 -0
- package/dist/grafastGraphql.d.ts +5 -5
- package/dist/graphqlCollectFields.d.ts +6 -0
- package/dist/graphqlCollectFields.js +14 -8
- package/dist/interfaces.d.ts +18 -0
- package/dist/interfaces.js +0 -2
- package/dist/mermaid.js +20 -11
- package/dist/operationPlan-input.js +8 -1
- package/dist/planJSONInterfaces.d.ts +2 -0
- package/dist/prepare.js +3 -3
- package/dist/step.d.ts +5 -5
- package/dist/step.js +53 -2
- package/dist/steps/__flag.d.ts +3 -2
- package/dist/steps/__inputObject.d.ts +1 -0
- package/dist/steps/__inputObject.js +36 -0
- package/dist/steps/__item.d.ts +3 -3
- package/dist/steps/__trackedValue.d.ts +8 -0
- package/dist/steps/__trackedValue.js +40 -0
- package/dist/steps/__value.d.ts +2 -1
- package/dist/steps/constant.d.ts +2 -0
- package/dist/steps/constant.js +12 -0
- package/dist/steps/graphqlResolver.js +8 -5
- package/dist/steps/listen.d.ts +2 -2
- package/dist/steps/object.d.ts +1 -1
- package/dist/steps/sideEffect.d.ts +0 -1
- package/dist/thereCanBeOnlyOne.js +1 -1
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,11 +51,8 @@ And please give some love to our featured sponsors 🤩:
|
|
|
51
51
|
<table><tr>
|
|
52
52
|
<td align="center"><a href="https://www.the-guild.dev/"><img src="https://graphile.org/images/sponsors/theguild.png" width="90" height="90" alt="The Guild" /><br />The Guild</a> *</td>
|
|
53
53
|
<td align="center"><a href="https://dovetailapp.com/"><img src="https://graphile.org/images/sponsors/dovetail.png" width="90" height="90" alt="Dovetail" /><br />Dovetail</a> *</td>
|
|
54
|
-
<td align="center"><a href="https://www.netflix.com/"><img src="https://graphile.org/images/sponsors/Netflix.png" width="90" height="90" alt="Netflix" /><br />Netflix</a> *</td>
|
|
55
54
|
<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
|
-
</tr><tr>
|
|
57
55
|
<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>
|
|
59
56
|
</tr></table>
|
|
60
57
|
|
|
61
58
|
<em>\* Sponsors the entire Graphile suite</em>
|
package/dist/args.js
CHANGED
|
@@ -5,6 +5,7 @@ const interfaces_js_1 = require("./interfaces.js");
|
|
|
5
5
|
const middleware_js_1 = require("./middleware.js");
|
|
6
6
|
const utils_js_1 = require("./utils.js");
|
|
7
7
|
const EMPTY_OBJECT = Object.freeze(Object.create(null));
|
|
8
|
+
const $$writeTest = Symbol("grafastWriteTest");
|
|
8
9
|
/**
|
|
9
10
|
* Applies Graphile Config hooks to your GraphQL request, e.g. to
|
|
10
11
|
* populate context or similar.
|
|
@@ -19,8 +20,13 @@ function hookArgs(rawArgs, legacyResolvedPreset, legacyCtx) {
|
|
|
19
20
|
rawArgs.requestContext = rawArgs.requestContext ?? legacyCtx;
|
|
20
21
|
}
|
|
21
22
|
const { middleware: rawMiddleware, resolvedPreset, contextValue: rawContextValue, } = rawArgs;
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
try {
|
|
24
|
+
rawContextValue[$$writeTest] = true;
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
// Make context mutable
|
|
28
|
+
rawArgs.contextValue = Object.assign(Object.create(null), rawContextValue);
|
|
29
|
+
}
|
|
24
30
|
const middleware = rawMiddleware === undefined && resolvedPreset != null
|
|
25
31
|
? (0, middleware_js_1.getGrafastMiddleware)(resolvedPreset)
|
|
26
32
|
: rawMiddleware ?? null;
|
package/dist/constraints.d.ts
CHANGED
|
@@ -27,6 +27,20 @@ interface ExistsConstraint {
|
|
|
27
27
|
path: (string | number)[];
|
|
28
28
|
exists: boolean;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* If `keys` is null: asserts that there is no value at the given
|
|
32
|
+
* path.
|
|
33
|
+
*
|
|
34
|
+
* Otherwise: asserts that the value at the given path has the exact same keys.
|
|
35
|
+
*/
|
|
36
|
+
interface KeysConstraint {
|
|
37
|
+
type: "keys";
|
|
38
|
+
path: (string | number)[];
|
|
39
|
+
/**
|
|
40
|
+
* If this is null it implies that the object did not exist.
|
|
41
|
+
*/
|
|
42
|
+
keys: ReadonlyArray<string> | null;
|
|
43
|
+
}
|
|
30
44
|
/**
|
|
31
45
|
* If `expectedLength` is null: asserts that there is no value at the given
|
|
32
46
|
* path.
|
|
@@ -52,7 +66,7 @@ interface IsEmptyConstraint {
|
|
|
52
66
|
path: (string | number)[];
|
|
53
67
|
isEmpty: boolean;
|
|
54
68
|
}
|
|
55
|
-
export type Constraint = ValueConstraint | EqualityConstraint | ExistsConstraint | LengthConstraint | IsEmptyConstraint;
|
|
69
|
+
export type Constraint = ValueConstraint | EqualityConstraint | ExistsConstraint | LengthConstraint | IsEmptyConstraint | KeysConstraint;
|
|
56
70
|
/**
|
|
57
71
|
* Implements the `MatchesConstraints` algorithm.
|
|
58
72
|
*/
|
package/dist/constraints.js
CHANGED
|
@@ -32,7 +32,8 @@ function matchesConstraint(constraint, object) {
|
|
|
32
32
|
const value = valueAtPath(object, constraint.path);
|
|
33
33
|
switch (constraint.type) {
|
|
34
34
|
case "length": {
|
|
35
|
-
|
|
35
|
+
const actualLength = Array.isArray(value) ? value.length : null;
|
|
36
|
+
return actualLength === constraint.expectedLength;
|
|
36
37
|
}
|
|
37
38
|
case "exists": {
|
|
38
39
|
return (value !== undefined) === constraint.exists;
|
|
@@ -49,6 +50,44 @@ function matchesConstraint(constraint, object) {
|
|
|
49
50
|
Object.keys(value).length === 0;
|
|
50
51
|
return isEmpty === constraint.isEmpty;
|
|
51
52
|
}
|
|
53
|
+
case "keys": {
|
|
54
|
+
const { keys: expectedKeys } = constraint;
|
|
55
|
+
if (expectedKeys === null) {
|
|
56
|
+
return value == null || typeof value !== "object";
|
|
57
|
+
}
|
|
58
|
+
else if (value == null || typeof value !== "object") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// keys are always in order of the gql type; see coerceInputValue and __InputObjectStep ctor
|
|
63
|
+
const valueKeys = Object.keys(value);
|
|
64
|
+
const valueKeyCount = valueKeys.length;
|
|
65
|
+
const expectedKeyCount = expectedKeys.length;
|
|
66
|
+
// Optimization: early bail
|
|
67
|
+
if (valueKeyCount < expectedKeyCount) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* This is `i` but adjusted so that `undefined` doesn't increment it.
|
|
72
|
+
* Should match index in `expectedKeys`.
|
|
73
|
+
*/
|
|
74
|
+
let definedRawKeyCount = 0;
|
|
75
|
+
for (let i = 0; i < valueKeyCount; i++) {
|
|
76
|
+
const valueKey = valueKeys[i];
|
|
77
|
+
if (value[valueKey] !== undefined) {
|
|
78
|
+
if (valueKey !== expectedKeys[definedRawKeyCount]) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
definedRawKeyCount++;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Make sure there aren't any additional expected keys
|
|
85
|
+
if (definedRawKeyCount !== expectedKeyCount) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
52
91
|
default: {
|
|
53
92
|
const never = constraint;
|
|
54
93
|
throw new Error(`Unsupported constraint type '${never.type}'`);
|
|
@@ -113,6 +113,7 @@ export declare class LayerPlan<TReason extends LayerPlanReason = LayerPlanReason
|
|
|
113
113
|
constructor(operationPlan: OperationPlan, parentLayerPlan: LayerPlan | null, reason: TReason);
|
|
114
114
|
toString(): string;
|
|
115
115
|
print(depth?: number): string;
|
|
116
|
+
_hasSetRootStep: boolean;
|
|
116
117
|
setRootStep($root: ExecutableStep): void;
|
|
117
118
|
finalize(): void;
|
|
118
119
|
newBucket(parentBucket: Bucket): Bucket | null;
|
package/dist/engine/LayerPlan.js
CHANGED
|
@@ -89,6 +89,23 @@ class LayerPlan {
|
|
|
89
89
|
this.steps = [];
|
|
90
90
|
/** @internal */
|
|
91
91
|
this.pendingSteps = [];
|
|
92
|
+
/**
|
|
93
|
+
* This goes along with `parentStep` in the reason, except it applies to all
|
|
94
|
+
* layer plan types and we figure it out automatically from the parent layer
|
|
95
|
+
* plan. If this step has an error at a given index, then it should be
|
|
96
|
+
* treated as if the parentStep had an error at that same index.
|
|
97
|
+
*
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
this.parentSideEffectStep = null;
|
|
101
|
+
/**
|
|
102
|
+
* This tracks the latest seen side effect at the current point in planning
|
|
103
|
+
* (such that created steps take this to be their implicitSideEffectStep).
|
|
104
|
+
* This isn't used once planning is complete.
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
this.latestSideEffectStep = null;
|
|
92
109
|
/**
|
|
93
110
|
* Describes the order in which the steps within this LayerPlan are executed.
|
|
94
111
|
*
|
|
@@ -97,6 +114,13 @@ class LayerPlan {
|
|
|
97
114
|
* @internal
|
|
98
115
|
*/
|
|
99
116
|
this.phases = [];
|
|
117
|
+
this._hasSetRootStep = false;
|
|
118
|
+
// This layer plan is dependent on the latest side effect. Note that when
|
|
119
|
+
// we set a `rootStep` later, if the root step is dependent on this step
|
|
120
|
+
// (directly or indirectly) we will clear this property.
|
|
121
|
+
this.parentSideEffectStep = parentLayerPlan?.latestSideEffectStep ?? null;
|
|
122
|
+
// There has yet to be any side effects created in this layer.
|
|
123
|
+
this.latestSideEffectStep = null;
|
|
100
124
|
this.stepsByConstructor = new Map();
|
|
101
125
|
if (parentLayerPlan !== null) {
|
|
102
126
|
this.depth = parentLayerPlan.depth + 1;
|
|
@@ -133,7 +157,7 @@ class LayerPlan {
|
|
|
133
157
|
? `{${this.reason.typeNames.join(",")}}`
|
|
134
158
|
: "";
|
|
135
159
|
const deps = this.copyStepIds.length > 0 ? `/${this.copyStepIds}` : "";
|
|
136
|
-
return `LayerPlan<${this.id}${chain}?${this.reason.type}${reasonExtra}!${this.rootStep?.id ?? "x"}${deps}>`;
|
|
160
|
+
return `LayerPlan<${this.id}${chain}${this.parentSideEffectStep ? `^${this.parentSideEffectStep.id}` : ""}?${this.reason.type}${reasonExtra}!${this.rootStep?.id ?? "x"}${deps}>`;
|
|
137
161
|
}
|
|
138
162
|
print(depth = 0) {
|
|
139
163
|
const output = [`${" ".repeat(depth * 2)}${this}`];
|
|
@@ -143,7 +167,15 @@ class LayerPlan {
|
|
|
143
167
|
return output.join("\n");
|
|
144
168
|
}
|
|
145
169
|
setRootStep($root) {
|
|
170
|
+
if (this._hasSetRootStep) {
|
|
171
|
+
throw new Error(`Set root step on ${this} more than once`);
|
|
172
|
+
}
|
|
173
|
+
this._hasSetRootStep = true;
|
|
146
174
|
this.operationPlan.stepTracker.setLayerPlanRootStep(this, $root);
|
|
175
|
+
// NOTE: we may clear `this.parentSideEffectStep` based on the `$root` step
|
|
176
|
+
// having an explicit dependency on `this.parentSideEffectStep`; but that
|
|
177
|
+
// will be done as part of `OperationPlan::finalizeLayerPlans()` because
|
|
178
|
+
// steps aren't assigned `implicitSideEffectStep`s until that point.
|
|
147
179
|
}
|
|
148
180
|
/** @internal Use plan.getStep(id) instead. */
|
|
149
181
|
getStep(id, requestingStep) {
|
|
@@ -176,6 +208,14 @@ class LayerPlan {
|
|
|
176
208
|
: [];
|
|
177
209
|
const iterators = this.reason.type === "mutationField" ? parentBucket.iterators : [];
|
|
178
210
|
const map = new Map();
|
|
211
|
+
const $parentSideEffect = this.parentSideEffectStep;
|
|
212
|
+
let parentSideEffectValue;
|
|
213
|
+
if ($parentSideEffect) {
|
|
214
|
+
parentSideEffectValue = parentBucket.store.get($parentSideEffect.id);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
parentSideEffectValue = null;
|
|
218
|
+
}
|
|
179
219
|
let size = 0;
|
|
180
220
|
switch (this.reason.type) {
|
|
181
221
|
case "nullableBoundary": {
|
|
@@ -194,15 +234,20 @@ class LayerPlan {
|
|
|
194
234
|
size = 0;
|
|
195
235
|
}
|
|
196
236
|
else {
|
|
197
|
-
size = parentBucket.size;
|
|
198
237
|
store.set(itemStepId, fieldValue);
|
|
199
238
|
for (const stepId of copyStepIds) {
|
|
200
239
|
store.set(stepId, parentBucket.store.get(stepId));
|
|
201
240
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
241
|
+
const parentBucketSize = parentBucket.size;
|
|
242
|
+
for (let originalIndex = 0; originalIndex < parentBucketSize; originalIndex++) {
|
|
243
|
+
if (parentSideEffectValue === null ||
|
|
244
|
+
!(parentSideEffectValue._flagsAt(originalIndex) & interfaces_js_1.FLAG_ERROR)) {
|
|
245
|
+
const newIndex = size++;
|
|
246
|
+
map.set(originalIndex, newIndex);
|
|
247
|
+
polymorphicPathList[newIndex] =
|
|
248
|
+
parentBucket.polymorphicPathList[originalIndex];
|
|
249
|
+
iterators[newIndex] = parentBucket.iterators[originalIndex];
|
|
250
|
+
}
|
|
206
251
|
}
|
|
207
252
|
}
|
|
208
253
|
}
|
|
@@ -244,10 +289,12 @@ class LayerPlan {
|
|
|
244
289
|
// than we have parent bucket entries (because we exclude nulls), so
|
|
245
290
|
// we must "multiply up" (down) the store entries.
|
|
246
291
|
for (let originalIndex = 0; originalIndex < parentBucket.size; originalIndex++) {
|
|
247
|
-
|
|
248
|
-
|
|
292
|
+
if ((parentSideEffectValue === null ||
|
|
293
|
+
!(parentSideEffectValue._flagsAt(originalIndex) & interfaces_js_1.FLAG_ERROR)) &&
|
|
294
|
+
!(nullableStepStore._flagsAt(originalIndex) & interfaces_js_1.FLAG_NULL)) {
|
|
249
295
|
const newIndex = size++;
|
|
250
296
|
map.set(originalIndex, newIndex);
|
|
297
|
+
const fieldValue = nullableStepStore.at(originalIndex);
|
|
251
298
|
itemStepIdList[newIndex] = fieldValue;
|
|
252
299
|
polymorphicPathList[newIndex] =
|
|
253
300
|
parentBucket.polymorphicPathList[originalIndex];
|
|
@@ -295,7 +342,9 @@ class LayerPlan {
|
|
|
295
342
|
// have parent buckets, so we must "multiply up" the store entries.
|
|
296
343
|
for (let originalIndex = 0; originalIndex < parentBucket.size; originalIndex++) {
|
|
297
344
|
const list = listStepStore.at(originalIndex);
|
|
298
|
-
if (
|
|
345
|
+
if ((parentSideEffectValue === null ||
|
|
346
|
+
!(parentSideEffectValue._flagsAt(originalIndex) & interfaces_js_1.FLAG_ERROR)) &&
|
|
347
|
+
Array.isArray(list)) {
|
|
299
348
|
const newIndexes = [];
|
|
300
349
|
map.set(originalIndex, newIndexes);
|
|
301
350
|
for (let j = 0, l = list.length; j < l; j++) {
|
|
@@ -356,6 +405,10 @@ class LayerPlan {
|
|
|
356
405
|
if (flags & (interfaces_js_1.FLAG_ERROR | interfaces_js_1.FLAG_INHIBITED | interfaces_js_1.FLAG_NULL)) {
|
|
357
406
|
continue;
|
|
358
407
|
}
|
|
408
|
+
if (parentSideEffectValue !== null &&
|
|
409
|
+
parentSideEffectValue._flagsAt(originalIndex) & interfaces_js_1.FLAG_ERROR) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
359
412
|
const value = polymorphicPlanStore.at(originalIndex);
|
|
360
413
|
const typeName = (0, polymorphic_js_1.resolveType)(value);
|
|
361
414
|
if (!targetTypeNames.includes(typeName)) {
|