grafast 0.1.1-beta.0 → 0.1.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/args.js +62 -0
- package/dist/assert.js +24 -0
- package/dist/bucket.js +4 -0
- package/dist/config.js +99 -0
- package/dist/constraints.js +72 -0
- package/dist/deferred.js +25 -0
- package/dist/dev.js +10 -0
- package/dist/engine/LayerPlan.js +536 -0
- package/dist/engine/OperationPlan.js +2543 -0
- package/dist/engine/OutputPlan.js +1059 -0
- package/dist/engine/StepTracker.js +524 -0
- package/dist/engine/executeBucket.js +700 -0
- package/dist/engine/executeOutputPlan.js +21 -0
- package/dist/engine/lib/withGlobalLayerPlan.js +36 -0
- package/dist/envelop.js +73 -5
- package/dist/error.js +62 -0
- package/dist/establishOperationPlan.js +186 -0
- package/dist/execute.js +80 -0
- package/dist/exportAs.js +57 -0
- package/dist/global.js +13 -0
- package/dist/grafastGraphql.js +133 -0
- package/dist/grafastPrint.js +177 -0
- package/dist/graphqlCollectFields.js +217 -0
- package/dist/graphqlMergeSelectionSets.js +60 -0
- package/dist/index.d.ts +161 -2
- package/dist/index.js +255 -499
- package/dist/input.js +159 -0
- package/dist/inspect.js +20 -0
- package/dist/interfaces.d.ts +3 -160
- package/dist/interfaces.js +63 -0
- package/dist/makeGrafastSchema.js +174 -0
- package/dist/mermaid.js +290 -7
- package/dist/operationPlan-input.js +449 -0
- package/dist/planJSONInterfaces.js +3 -0
- package/dist/polymorphic.js +73 -0
- package/dist/prepare.js +763 -0
- package/dist/step.js +396 -0
- package/dist/steps/__inputDynamicScalar.js +125 -0
- package/dist/steps/__inputList.js +110 -0
- package/dist/steps/__inputObject.js +128 -0
- package/dist/steps/__inputStaticLeaf.js +43 -0
- package/dist/steps/__item.js +50 -0
- package/dist/steps/__trackedValue.js +257 -0
- package/dist/steps/__value.js +47 -0
- package/dist/steps/access.js +183 -0
- package/dist/steps/applyTransforms.js +151 -0
- package/dist/steps/connection.js +315 -0
- package/dist/steps/constant.js +72 -0
- package/dist/steps/each.js +64 -0
- package/dist/steps/error.js +32 -0
- package/dist/steps/filter.js +32 -0
- package/dist/steps/first.js +49 -0
- package/dist/steps/graphqlResolver.js +260 -0
- package/dist/steps/groupBy.js +40 -0
- package/dist/steps/index.js +117 -0
- package/dist/steps/lambda.js +58 -0
- package/dist/steps/last.js +41 -0
- package/dist/steps/list.js +75 -0
- package/dist/steps/listTransform.js +185 -0
- package/dist/steps/listen.js +61 -0
- package/dist/steps/load.d.ts +4 -4
- package/dist/steps/load.js +310 -0
- package/dist/steps/node.d.ts +6 -6
- package/dist/steps/node.js +107 -0
- package/dist/steps/object.js +195 -0
- package/dist/steps/partitionByIndex.js +86 -0
- package/dist/steps/proxy.js +114 -0
- package/dist/steps/remapKeys.js +84 -0
- package/dist/steps/reverse.js +52 -0
- package/dist/steps/setter.js +28 -0
- package/dist/stringifyPayload.js +59 -0
- package/dist/stripAnsi.js +14 -0
- package/dist/subscribe.js +14 -0
- package/dist/thereCanBeOnlyOne.js +21 -0
- package/dist/timeSource.js +8 -0
- package/dist/utils.js +698 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +6 -0
- package/package.json +9 -4
- package/dist/LICENSES.txt +0 -300
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeOutputPlan = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const assert = tslib_1.__importStar(require("../assert.js"));
|
|
6
|
+
const dev_js_1 = require("../dev.js");
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
function executeOutputPlan(ctx, outputPlan, bucket, bucketIndex, outputDataAsString) {
|
|
11
|
+
if (dev_js_1.isDev) {
|
|
12
|
+
assert.strictEqual(bucket.isComplete, true, "Can only process an output plan for a completed bucket");
|
|
13
|
+
}
|
|
14
|
+
// PERF: feels like this path could be done more efficiently
|
|
15
|
+
const mutablePath = ["SOMEONE_FORGOT_TO_SLICE_THE_PATH!", ...ctx.path];
|
|
16
|
+
return outputDataAsString
|
|
17
|
+
? outputPlan.executeString(ctx.root, mutablePath, bucket, bucketIndex)
|
|
18
|
+
: outputPlan.execute(ctx.root, mutablePath, bucket, bucketIndex);
|
|
19
|
+
}
|
|
20
|
+
exports.executeOutputPlan = executeOutputPlan;
|
|
21
|
+
//# sourceMappingURL=executeOutputPlan.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.currentPolymorphicPaths = exports.currentLayerPlan = exports.withGlobalLayerPlan = void 0;
|
|
4
|
+
let globalData_layerPlan = undefined;
|
|
5
|
+
let globalData_polymorphicPaths = undefined;
|
|
6
|
+
function withGlobalLayerPlan(layerPlan, polymorphicPaths, callback, callbackThis, ...callbackArgs) {
|
|
7
|
+
const oldLayerPlan = globalData_layerPlan;
|
|
8
|
+
globalData_layerPlan = layerPlan;
|
|
9
|
+
const oldPolymorphicPaths = globalData_polymorphicPaths;
|
|
10
|
+
globalData_polymorphicPaths = polymorphicPaths;
|
|
11
|
+
try {
|
|
12
|
+
return callback.apply(callbackThis, callbackArgs);
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
globalData_layerPlan = oldLayerPlan;
|
|
16
|
+
globalData_polymorphicPaths = oldPolymorphicPaths;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.withGlobalLayerPlan = withGlobalLayerPlan;
|
|
20
|
+
function currentLayerPlan() {
|
|
21
|
+
if (globalData_layerPlan === undefined) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
// Must only be called from inside `withGlobalLayerPlan`!
|
|
24
|
+
"Now is not a valid time to call `currentLayerPlan`. This error typically occurs when you attempt to call a Grafast step function from outside of the planning lifecycle - it's important to note that Grafast plans must be resolved synchronously, so check for 'async' or 'setTimeout' or any location where a step function is called outside of a plan resolver. For more information, read about plan resolvers: https://grafast.org/grafast/plan-resolvers");
|
|
25
|
+
}
|
|
26
|
+
return globalData_layerPlan;
|
|
27
|
+
}
|
|
28
|
+
exports.currentLayerPlan = currentLayerPlan;
|
|
29
|
+
function currentPolymorphicPaths() {
|
|
30
|
+
if (globalData_polymorphicPaths === undefined) {
|
|
31
|
+
throw new Error("GrafastInternalError<b0b05743-8b21-42c6-9b53-925013d88bd1>: currentPolymorphicPaths called out of turn; must only called within a withGlobalLayerPlan callback");
|
|
32
|
+
}
|
|
33
|
+
return globalData_polymorphicPaths;
|
|
34
|
+
}
|
|
35
|
+
exports.currentPolymorphicPaths = currentPolymorphicPaths;
|
|
36
|
+
//# sourceMappingURL=withGlobalLayerPlan.js.map
|
package/dist/envelop.js
CHANGED
|
@@ -1,5 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMoreDetailedErrors = exports.useGrafast = void 0;
|
|
4
|
+
const execute_js_1 = require("./execute.js");
|
|
5
|
+
const stripAnsi_js_1 = require("./stripAnsi.js");
|
|
6
|
+
const subscribe_js_1 = require("./subscribe.js");
|
|
7
|
+
function processExplain(explainAllowed, explainHeaders) {
|
|
8
|
+
if (explainAllowed === false ||
|
|
9
|
+
(Array.isArray(explainAllowed) && explainAllowed.length === 0)) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const explainHeader = Array.isArray(explainHeaders)
|
|
13
|
+
? explainHeaders.join(",")
|
|
14
|
+
: explainHeaders;
|
|
15
|
+
if (!explainHeader) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const explainParts = explainHeader.split(",");
|
|
19
|
+
if (explainAllowed === true || explainAllowed === undefined) {
|
|
20
|
+
return explainParts;
|
|
21
|
+
}
|
|
22
|
+
// Assumption: explainAllowed is relatively short (and unique).
|
|
23
|
+
// PERF: there's probably a faster way to do this...
|
|
24
|
+
return explainAllowed.filter((p) => explainParts.includes(p));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* An Envelop plugin that uses Grafast to prepare and execute the GraphQL
|
|
28
|
+
* query.
|
|
29
|
+
*/
|
|
30
|
+
const useGrafast = (options = {}) => {
|
|
31
|
+
const { explainAllowed = true } = options;
|
|
32
|
+
return {
|
|
33
|
+
async onExecute(opts) {
|
|
34
|
+
const explainHeaders = opts.args.contextValue?.req?.headers["x-graphql-explain"];
|
|
35
|
+
const explain = processExplain(explainAllowed, explainHeaders);
|
|
36
|
+
opts.setExecuteFn((args) => (0, execute_js_1.execute)(args, {
|
|
37
|
+
grafast: { explain },
|
|
38
|
+
}));
|
|
39
|
+
},
|
|
40
|
+
async onSubscribe(opts) {
|
|
41
|
+
const ctx = opts.args.contextValue;
|
|
42
|
+
const explainHeaders = (ctx?.req?.headers ||
|
|
43
|
+
ctx?.request?.headers ||
|
|
44
|
+
ctx?.connectionParams)?.["x-graphql-explain"];
|
|
45
|
+
const explain = processExplain(explainAllowed, explainHeaders);
|
|
46
|
+
opts.setSubscribeFn(async (args) => (0, subscribe_js_1.subscribe)(args, {
|
|
47
|
+
grafast: { explain },
|
|
48
|
+
}));
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
exports.useGrafast = useGrafast;
|
|
53
|
+
/**
|
|
54
|
+
* An Envelop plugin that will make any GraphQL errors easier to read from
|
|
55
|
+
* inside of GraphiQL.
|
|
56
|
+
*/
|
|
57
|
+
const useMoreDetailedErrors = () => ({
|
|
58
|
+
onExecute: () => ({
|
|
59
|
+
onExecuteDone({ result }) {
|
|
60
|
+
if ("errors" in result && result.errors) {
|
|
61
|
+
result.errors = result.errors.map((e) => {
|
|
62
|
+
const obj = e.toJSON();
|
|
63
|
+
return Object.assign(obj, {
|
|
64
|
+
message: (0, stripAnsi_js_1.stripAnsi)(obj.message),
|
|
65
|
+
extensions: { stack: (0, stripAnsi_js_1.stripAnsi)(e.stack ?? "").split("\n") },
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
exports.useMoreDetailedErrors = useMoreDetailedErrors;
|
|
73
|
+
//# sourceMappingURL=envelop.js.map
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a, _b;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.isSafeError = exports.SafeError = exports.isGrafastError = exports.newGrafastError = exports._GrafastError = exports.$$error = void 0;
|
|
5
|
+
const interfaces_js_1 = require("./interfaces.js");
|
|
6
|
+
exports.$$error = Symbol("isGrafastError");
|
|
7
|
+
// IMPORTANT: this WILL NOT WORK when compiled down to ES5. It requires ES6+
|
|
8
|
+
// native class support.
|
|
9
|
+
/**
|
|
10
|
+
* When an error occurs during plan execution we wrap it in a GrafastError so
|
|
11
|
+
* that we can pass it around as a value. It gets unwrapped and thrown in the
|
|
12
|
+
* grafast resolver.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
const _GrafastError = class GrafastError extends Error {
|
|
17
|
+
static { _a = exports.$$error; }
|
|
18
|
+
constructor(originalError, planId) {
|
|
19
|
+
if (originalError instanceof exports._GrafastError) {
|
|
20
|
+
throw new Error("GrafastInternalError<62505509-8b21-4ef7-80f5-d0f99873174b>: attempted to wrap a GrafastError with a GrafastError.");
|
|
21
|
+
}
|
|
22
|
+
const message = originalError?.message;
|
|
23
|
+
super(message);
|
|
24
|
+
this[_a] = true;
|
|
25
|
+
Object.setPrototypeOf(this, GrafastError.prototype);
|
|
26
|
+
this.originalError = originalError;
|
|
27
|
+
this.extensions = { grafast: { planId } };
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
exports._GrafastError = _GrafastError;
|
|
31
|
+
/**
|
|
32
|
+
* DO NOT ALLOW CONSTRUCTION OF ERRORS OUTSIDE OF THIS MODULE!
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
function newGrafastError(error, planId) {
|
|
37
|
+
return new exports._GrafastError(error, planId);
|
|
38
|
+
}
|
|
39
|
+
exports.newGrafastError = newGrafastError;
|
|
40
|
+
/**
|
|
41
|
+
* Is the given value a GrafastError? This is the only public API that people
|
|
42
|
+
* should use for looking at GrafastErrors.
|
|
43
|
+
*/
|
|
44
|
+
function isGrafastError(value) {
|
|
45
|
+
return typeof value === "object" && value !== null && exports.$$error in value;
|
|
46
|
+
}
|
|
47
|
+
exports.isGrafastError = isGrafastError;
|
|
48
|
+
class SafeError extends Error {
|
|
49
|
+
static { _b = interfaces_js_1.$$safeError; }
|
|
50
|
+
constructor(message, extensions = undefined, errorOptions) {
|
|
51
|
+
super(message, errorOptions);
|
|
52
|
+
this.extensions = extensions;
|
|
53
|
+
this[_b] = true;
|
|
54
|
+
Object.setPrototypeOf(this, SafeError.prototype);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.SafeError = SafeError;
|
|
58
|
+
function isSafeError(error) {
|
|
59
|
+
return error[interfaces_js_1.$$safeError];
|
|
60
|
+
}
|
|
61
|
+
exports.isSafeError = isSafeError;
|
|
62
|
+
//# sourceMappingURL=error.js.map
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.establishOperationPlan = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lru_1 = tslib_1.__importDefault(require("@graphile/lru"));
|
|
6
|
+
const debug_1 = tslib_1.__importDefault(require("debug"));
|
|
7
|
+
const constraints_js_1 = require("./constraints.js");
|
|
8
|
+
const dev_js_1 = require("./dev.js");
|
|
9
|
+
const index_js_1 = require("./index.js");
|
|
10
|
+
const interfaces_js_1 = require("./interfaces.js");
|
|
11
|
+
const timeSource_js_1 = require("./timeSource.js");
|
|
12
|
+
const debug = (0, debug_1.default)("grafast:establishOperationPlan");
|
|
13
|
+
// How long is a timeout valid for? Here I've set it to 60 seconds.
|
|
14
|
+
const TIMEOUT_TIMEOUT = (typeof process !== "undefined" &&
|
|
15
|
+
typeof process.env.GRAFAST_TIMEOUT_VALIDITY_MS === "string"
|
|
16
|
+
? parseInt(process.env.GRAFAST_TIMEOUT_VALIDITY_MS, 10)
|
|
17
|
+
: null) || 60000;
|
|
18
|
+
// OPTIMIZE: we should consider increasing the timeout once the process has been
|
|
19
|
+
// running a while (since the JIT should have kicked in) - we could even use
|
|
20
|
+
// `setTimeout` to trigger it after certain amount of time elapsed.
|
|
21
|
+
/**
|
|
22
|
+
* This is a development-only validation to check fragments do, in fact, match
|
|
23
|
+
* - even if the objects themselves differ.
|
|
24
|
+
*/
|
|
25
|
+
function reallyAssertFragmentsMatch(oldFragments, fragments) {
|
|
26
|
+
if (oldFragments !== fragments) {
|
|
27
|
+
debug("fragments aren't `===` for same operation");
|
|
28
|
+
// Consistency check - we assume that if the operation is the same then
|
|
29
|
+
// the fragments will be, but this may not be the case depending on if
|
|
30
|
+
// GraphQL.js caches the operation node.
|
|
31
|
+
const oldKeys = Object.keys(oldFragments).sort();
|
|
32
|
+
const newKeys = Object.keys(fragments).sort();
|
|
33
|
+
const oldKeyStr = oldKeys.join(",");
|
|
34
|
+
const newKeyStr = newKeys.join(",");
|
|
35
|
+
if (oldKeyStr.length !== newKeyStr.length) {
|
|
36
|
+
throw new Error(`Inconsistency error: operation matches, but fragment keys differ: '${oldKeyStr}' != '${newKeyStr}'.`);
|
|
37
|
+
}
|
|
38
|
+
for (const key of newKeys) {
|
|
39
|
+
if (oldFragments[key] !== fragments[key]) {
|
|
40
|
+
throw new Error(`Inconsistency error: operation matches, fragment names match, but fragment '${key}' is not '===' to the previous value.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Optimise this away in production.
|
|
46
|
+
const assertFragmentsMatch = !dev_js_1.isDev ? dev_js_1.noop : reallyAssertFragmentsMatch;
|
|
47
|
+
/**
|
|
48
|
+
* Implements the `IsOpPlanCompatible` algorithm.
|
|
49
|
+
*
|
|
50
|
+
* @remarks Due to the optimisation in `establishOperationPlan`, the schema, document
|
|
51
|
+
* and operationName checks have already been performed.
|
|
52
|
+
*/
|
|
53
|
+
function isOperationPlanCompatible(operationPlan, variableValues, context, rootValue) {
|
|
54
|
+
const { variableValuesConstraints, contextConstraints, rootValueConstraints, } = operationPlan;
|
|
55
|
+
if (!(0, constraints_js_1.matchesConstraints)(variableValuesConstraints, variableValues)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (!(0, constraints_js_1.matchesConstraints)(contextConstraints, context)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (!(0, constraints_js_1.matchesConstraints)(rootValueConstraints, rootValue)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Implements the `EstablishOpPlan` algorithm.
|
|
68
|
+
*
|
|
69
|
+
* @remarks Though EstablishOpPlan accepts document and operationName, we
|
|
70
|
+
* instead accept operation and fragments since they're easier to get a hold of
|
|
71
|
+
* in GraphQL.js.
|
|
72
|
+
*/
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
74
|
+
function establishOperationPlan(schema, operation, fragments, variableValues, context, rootValue, planningTimeout = null) {
|
|
75
|
+
let cacheByOperation = schema.extensions.grafast?.[interfaces_js_1.$$cacheByOperation];
|
|
76
|
+
let cache = cacheByOperation?.get(operation);
|
|
77
|
+
// These two variables to make it easy to trim the linked list later.
|
|
78
|
+
let count = 0;
|
|
79
|
+
let lastButOneItem = null;
|
|
80
|
+
if (cache !== undefined) {
|
|
81
|
+
// Dev-only validation
|
|
82
|
+
assertFragmentsMatch(cache.fragments, fragments);
|
|
83
|
+
let previousItem = null;
|
|
84
|
+
let linkedItem = cache.possibleOperationPlans;
|
|
85
|
+
while (linkedItem) {
|
|
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;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
cache.possibleOperationPlans = linkedItem;
|
|
97
|
+
}
|
|
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
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// That's Not My Timeout, let's try again.
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
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;
|
|
127
|
+
}
|
|
128
|
+
// We found a suitable OperationPlan - use that!
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
count++;
|
|
132
|
+
lastButOneItem = previousItem;
|
|
133
|
+
previousItem = linkedItem;
|
|
134
|
+
linkedItem = linkedItem.next;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// No suitable OperationPlan found, time to make one.
|
|
138
|
+
let operationPlan;
|
|
139
|
+
let error;
|
|
140
|
+
try {
|
|
141
|
+
operationPlan = new index_js_1.OperationPlan(schema, operation, fragments, variableValues, context, rootValue, planningTimeout);
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
error = e;
|
|
145
|
+
}
|
|
146
|
+
// Store it to the cache
|
|
147
|
+
if (!cacheByOperation) {
|
|
148
|
+
if (!schema.extensions.grafast) {
|
|
149
|
+
schema.extensions.grafast = Object.create(null);
|
|
150
|
+
}
|
|
151
|
+
cacheByOperation = new lru_1.default({
|
|
152
|
+
maxLength: schema.extensions.grafast.operationsCacheMaxLength ?? 500,
|
|
153
|
+
});
|
|
154
|
+
schema.extensions.grafast[interfaces_js_1.$$cacheByOperation] = cacheByOperation;
|
|
155
|
+
}
|
|
156
|
+
const operationPlanOrError = operationPlan ?? error;
|
|
157
|
+
if (!cache) {
|
|
158
|
+
cache = {
|
|
159
|
+
fragments,
|
|
160
|
+
possibleOperationPlans: { value: operationPlanOrError, next: null },
|
|
161
|
+
};
|
|
162
|
+
cacheByOperation.set(operation, cache);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const max = schema.extensions.grafast.operationOperationPlansCacheMaxLength ?? 50;
|
|
166
|
+
if (count >= max) {
|
|
167
|
+
// Remove the tail to ensure we never grow too big
|
|
168
|
+
lastButOneItem.next = null;
|
|
169
|
+
count--;
|
|
170
|
+
// LOGGING: we should announce this so that people know there's something that needs fixing in their schema (too much eval?)
|
|
171
|
+
}
|
|
172
|
+
// Add new operationPlan to top of the linked list.
|
|
173
|
+
cache.possibleOperationPlans = {
|
|
174
|
+
value: operationPlanOrError,
|
|
175
|
+
next: cache.possibleOperationPlans,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (error !== undefined) {
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
return operationPlan;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.establishOperationPlan = establishOperationPlan;
|
|
186
|
+
//# sourceMappingURL=establishOperationPlan.js.map
|
package/dist/execute.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.execute = exports.withGrafastArgs = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const eventemitter3_1 = tslib_1.__importDefault(require("eventemitter3"));
|
|
6
|
+
const config_js_1 = require("./config.js");
|
|
7
|
+
const inspect_js_1 = require("./inspect.js");
|
|
8
|
+
const interfaces_js_1 = require("./interfaces.js");
|
|
9
|
+
const prepare_js_1 = require("./prepare.js");
|
|
10
|
+
const utils_js_1 = require("./utils.js");
|
|
11
|
+
const isDev = typeof process !== "undefined" && process.env.NODE_ENV === "development";
|
|
12
|
+
/**
|
|
13
|
+
* Used by `execute` and `subscribe`.
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
function withGrafastArgs(args, resolvedPreset, outputDataAsString) {
|
|
17
|
+
const options = resolvedPreset?.grafast;
|
|
18
|
+
if (isDev) {
|
|
19
|
+
if (args.rootValue != null &&
|
|
20
|
+
(typeof args.rootValue !== "object" ||
|
|
21
|
+
Object.keys(args.rootValue).length > 0)) {
|
|
22
|
+
throw new Error(`Grafast executor doesn't support there being a rootValue (found ${(0, inspect_js_1.inspect)(args.rootValue)})`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (args.rootValue == null) {
|
|
26
|
+
args.rootValue = Object.create(null);
|
|
27
|
+
}
|
|
28
|
+
if (typeof args.rootValue !== "object" || args.rootValue == null) {
|
|
29
|
+
throw new Error("Grafast requires that the 'rootValue' be an object");
|
|
30
|
+
}
|
|
31
|
+
const explain = options?.explain;
|
|
32
|
+
const shouldExplain = !!explain;
|
|
33
|
+
let unlisten = null;
|
|
34
|
+
if (shouldExplain) {
|
|
35
|
+
const eventEmitter = new eventemitter3_1.default();
|
|
36
|
+
const explainOperations = [];
|
|
37
|
+
args.rootValue = Object.assign(Object.create(null), args.rootValue, {
|
|
38
|
+
[interfaces_js_1.$$eventEmitter]: eventEmitter,
|
|
39
|
+
[interfaces_js_1.$$extensions]: {
|
|
40
|
+
explain: {
|
|
41
|
+
operations: explainOperations,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const handleExplainOperation = ({ operation, }) => {
|
|
46
|
+
if (explain === true || (explain && explain.includes(operation.type))) {
|
|
47
|
+
explainOperations.push(operation);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
eventEmitter.on("explainOperation", handleExplainOperation);
|
|
51
|
+
unlisten = () => {
|
|
52
|
+
eventEmitter.removeListener("explainOperation", handleExplainOperation);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const rootValue = (0, prepare_js_1.grafastPrepare)(args, {
|
|
56
|
+
explain: options?.explain,
|
|
57
|
+
outputDataAsString,
|
|
58
|
+
timeouts: options?.timeouts,
|
|
59
|
+
});
|
|
60
|
+
if (unlisten !== null) {
|
|
61
|
+
Promise.resolve(rootValue).then(unlisten, unlisten);
|
|
62
|
+
}
|
|
63
|
+
// Convert from PromiseOrDirect to PromiseOrValue
|
|
64
|
+
if ((0, utils_js_1.isPromiseLike)(rootValue)) {
|
|
65
|
+
return Promise.resolve(rootValue);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return rootValue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.withGrafastArgs = withGrafastArgs;
|
|
72
|
+
/**
|
|
73
|
+
* Use this instead of GraphQL.js' execute method and we'll automatically
|
|
74
|
+
* run grafastPrepare for you and handle the result.
|
|
75
|
+
*/
|
|
76
|
+
function execute(args, resolvedPreset = config_js_1.NULL_PRESET, outputDataAsString = false) {
|
|
77
|
+
return withGrafastArgs(args, resolvedPreset, outputDataAsString);
|
|
78
|
+
}
|
|
79
|
+
exports.execute = execute;
|
|
80
|
+
//# sourceMappingURL=execute.js.map
|
package/dist/exportAs.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exportAsMany = exports.exportAs = void 0;
|
|
4
|
+
const utils_js_1 = require("./utils.js");
|
|
5
|
+
/**
|
|
6
|
+
* Marks that `thing` is exported from the `grafast` module as
|
|
7
|
+
* `exportName` so that `graphile-export` can convert references to `thing`
|
|
8
|
+
* into an `import` statement.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
function exportAs(moduleName, thing, exportName) {
|
|
13
|
+
if (!("$$export" in thing)) {
|
|
14
|
+
Object.defineProperty(thing, "$$export", {
|
|
15
|
+
value: { moduleName, exportName },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const e = thing.$$export;
|
|
20
|
+
if (e.moduleName !== moduleName) {
|
|
21
|
+
throw new Error(`Attempted to export ${thing} as '${moduleName}.${exportName}', but it's already exported as '${e.moduleName}.${e.exportName}' (module name mismatch)`);
|
|
22
|
+
}
|
|
23
|
+
if (typeof e.exportName === "string" || exportName === "string") {
|
|
24
|
+
if (e.exportName !== exportName) {
|
|
25
|
+
throw new Error(`Attempted to export ${thing} as '${moduleName}.${exportName}', but it's already exported as '${e.moduleName}.${e.exportName}' (export name mismatch)`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Must be arrays
|
|
30
|
+
if (!Array.isArray(e.exportName) ||
|
|
31
|
+
!Array.isArray(exportName) ||
|
|
32
|
+
!(0, utils_js_1.arraysMatch)(e.exportName, exportName)) {
|
|
33
|
+
throw new Error(`Attempted to export ${thing} as '${moduleName}.${exportName}', but it's already exported as '${e.moduleName}.${e.exportName}' (export name path mismatch)`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return thing;
|
|
38
|
+
}
|
|
39
|
+
exports.exportAs = exportAs;
|
|
40
|
+
/**
|
|
41
|
+
* Marks that each value in `all` is exported from the `grafast`
|
|
42
|
+
* module as the key in the `all` object so that `graphile-export` can
|
|
43
|
+
* convert references to these values into `import` statements.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
function exportAsMany(moduleName, all) {
|
|
48
|
+
for (const key of Object.keys(all)) {
|
|
49
|
+
const value = all[key];
|
|
50
|
+
if ((typeof value === "object" || typeof value === "function") &&
|
|
51
|
+
value !== null) {
|
|
52
|
+
exportAs(moduleName, all[key], key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.exportAsMany = exportAsMany;
|
|
57
|
+
//# sourceMappingURL=exportAs.js.map
|
package/dist/global.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDebug = exports.setDebug = void 0;
|
|
4
|
+
let debug = false;
|
|
5
|
+
function setDebug(newDebug) {
|
|
6
|
+
debug = newDebug;
|
|
7
|
+
}
|
|
8
|
+
exports.setDebug = setDebug;
|
|
9
|
+
function getDebug() {
|
|
10
|
+
return debug;
|
|
11
|
+
}
|
|
12
|
+
exports.getDebug = getDebug;
|
|
13
|
+
//# sourceMappingURL=global.js.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.grafastSync = exports.grafast = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lru_1 = tslib_1.__importDefault(require("@graphile/lru"));
|
|
6
|
+
const graphql = tslib_1.__importStar(require("graphql"));
|
|
7
|
+
const error_js_1 = require("./error.js");
|
|
8
|
+
const execute_js_1 = require("./execute.js");
|
|
9
|
+
const index_js_1 = require("./index.js");
|
|
10
|
+
const interfaces_js_1 = require("./interfaces.js");
|
|
11
|
+
const utils_js_1 = require("./utils.js");
|
|
12
|
+
const { GraphQLError, parse, Source, validate, validateSchema } = graphql;
|
|
13
|
+
/** Rough average size per query */
|
|
14
|
+
const CACHE_MULTIPLIER = 100000;
|
|
15
|
+
const MEGABYTE = 1024 * 1024;
|
|
16
|
+
const queryCacheMaxSizeInBytes = 50 * MEGABYTE;
|
|
17
|
+
const defaultQueryCacheMaxSize = Math.max(2, Math.ceil(queryCacheMaxSizeInBytes / CACHE_MULTIPLIER));
|
|
18
|
+
// If we can use crypto to create a hash, great. Otherwise just use the string.
|
|
19
|
+
let calculateQueryHash;
|
|
20
|
+
try {
|
|
21
|
+
let lastString;
|
|
22
|
+
let lastHash;
|
|
23
|
+
const createHash = require("crypto").createHash;
|
|
24
|
+
if (typeof createHash !== "function") {
|
|
25
|
+
throw new Error("Failed to load createHash");
|
|
26
|
+
}
|
|
27
|
+
calculateQueryHash = (queryString) => {
|
|
28
|
+
if (queryString !== lastString) {
|
|
29
|
+
lastString = queryString;
|
|
30
|
+
lastHash = createHash("sha1").update(queryString).digest("base64");
|
|
31
|
+
}
|
|
32
|
+
return lastHash;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
calculateQueryHash = (str) => str;
|
|
37
|
+
}
|
|
38
|
+
const parseAndValidate = (gqlSchema, stringOrSource) => {
|
|
39
|
+
let queryCache = gqlSchema.extensions.grafast?.[interfaces_js_1.$$queryCache];
|
|
40
|
+
if (!queryCache) {
|
|
41
|
+
const cacheSize = gqlSchema.extensions.grafast?.queryCacheMaxLength ??
|
|
42
|
+
defaultQueryCacheMaxSize;
|
|
43
|
+
queryCache = new lru_1.default({ maxLength: cacheSize });
|
|
44
|
+
if (!gqlSchema.extensions.grafast) {
|
|
45
|
+
gqlSchema.extensions.grafast = Object.create(null);
|
|
46
|
+
}
|
|
47
|
+
gqlSchema.extensions.grafast[interfaces_js_1.$$queryCache] = queryCache;
|
|
48
|
+
}
|
|
49
|
+
// Only cache queries that are less than 100kB, we don't want DOS attacks
|
|
50
|
+
// attempting to exhaust our memory.
|
|
51
|
+
const hash = calculateQueryHash(typeof stringOrSource === "string" ? stringOrSource : stringOrSource.body);
|
|
52
|
+
const result = queryCache.get(hash);
|
|
53
|
+
if (result !== undefined) {
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const source = typeof stringOrSource === "string"
|
|
58
|
+
? new Source(stringOrSource, "GraphQL Http Request")
|
|
59
|
+
: stringOrSource;
|
|
60
|
+
let queryDocumentAst;
|
|
61
|
+
// Catch an errors while parsing so that we can set the `statusCode` to
|
|
62
|
+
// 400. Otherwise we don’t need to parse this way.
|
|
63
|
+
try {
|
|
64
|
+
queryDocumentAst = parse(source);
|
|
65
|
+
// Validate our GraphQL query using given rules.
|
|
66
|
+
const validationErrors = validate(gqlSchema, queryDocumentAst);
|
|
67
|
+
const cacheResult = validationErrors.length > 0 ? validationErrors : queryDocumentAst;
|
|
68
|
+
queryCache.set(hash, cacheResult);
|
|
69
|
+
return cacheResult;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const cacheResult = [
|
|
73
|
+
error instanceof GraphQLError
|
|
74
|
+
? error
|
|
75
|
+
: new GraphQLError("Validation error occurred", undefined, undefined, undefined, undefined, error),
|
|
76
|
+
];
|
|
77
|
+
queryCache.set(hash, cacheResult);
|
|
78
|
+
return cacheResult;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* A replacement for GraphQL.js' `graphql` method that calls Grafast's
|
|
84
|
+
* execute instead
|
|
85
|
+
*/
|
|
86
|
+
function grafast(args, legacyResolvedPreset, legacyCtx) {
|
|
87
|
+
const { schema, source, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver, resolvedPreset = legacyResolvedPreset, requestContext = legacyCtx, } = args;
|
|
88
|
+
// Validate Schema
|
|
89
|
+
const schemaValidationErrors = validateSchema(schema);
|
|
90
|
+
if (schemaValidationErrors.length > 0) {
|
|
91
|
+
return { errors: schemaValidationErrors };
|
|
92
|
+
}
|
|
93
|
+
// Cached parse and validate
|
|
94
|
+
const documentOrErrors = parseAndValidate(schema, source);
|
|
95
|
+
if (Array.isArray(documentOrErrors)) {
|
|
96
|
+
return { errors: documentOrErrors };
|
|
97
|
+
}
|
|
98
|
+
const document = documentOrErrors;
|
|
99
|
+
const executionArgs = {
|
|
100
|
+
schema,
|
|
101
|
+
document,
|
|
102
|
+
rootValue,
|
|
103
|
+
contextValue,
|
|
104
|
+
variableValues,
|
|
105
|
+
operationName,
|
|
106
|
+
fieldResolver,
|
|
107
|
+
typeResolver,
|
|
108
|
+
};
|
|
109
|
+
if (resolvedPreset && requestContext) {
|
|
110
|
+
const argsOrPromise = (0, index_js_1.hookArgs)(executionArgs, resolvedPreset, requestContext);
|
|
111
|
+
if ((0, utils_js_1.isPromiseLike)(argsOrPromise)) {
|
|
112
|
+
return Promise.resolve(argsOrPromise).then((hookedArgs) => (0, execute_js_1.execute)(hookedArgs, resolvedPreset));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Execute
|
|
116
|
+
return (0, execute_js_1.execute)(argsOrPromise, resolvedPreset);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Execute
|
|
121
|
+
return (0, execute_js_1.execute)(executionArgs, resolvedPreset);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.grafast = grafast;
|
|
125
|
+
function grafastSync(args, legacyResolvedPreset, legacyRequestContext) {
|
|
126
|
+
const result = grafast(args, legacyResolvedPreset, legacyRequestContext);
|
|
127
|
+
if ((0, utils_js_1.isPromiseLike)(result)) {
|
|
128
|
+
throw new error_js_1.SafeError("Grafast execution failed to complete synchronously.");
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
exports.grafastSync = grafastSync;
|
|
133
|
+
//# sourceMappingURL=grafastGraphql.js.map
|