@envelop/core 2.5.1-alpha-20220824165935-4720f031 → 2.6.0
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/cjs/create.js +5 -7
- package/cjs/index.js +3 -3
- package/cjs/orchestrator.js +16 -24
- package/cjs/plugins/use-immediate-introspection.js +50 -0
- package/cjs/plugins/use-masked-errors.js +93 -0
- package/cjs/plugins/use-timing.js +138 -0
- package/cjs/traced-orchestrator.js +136 -0
- package/cjs/traced-schema.js +68 -0
- package/cjs/utils.js +26 -1
- package/esm/create.js +5 -7
- package/esm/index.js +3 -1
- package/esm/orchestrator.js +5 -13
- package/esm/plugins/use-immediate-introspection.js +46 -0
- package/esm/plugins/use-masked-errors.js +87 -0
- package/esm/plugins/use-timing.js +134 -0
- package/esm/traced-orchestrator.js +132 -0
- package/esm/traced-schema.js +64 -0
- package/esm/utils.js +22 -0
- package/package.json +6 -2
- package/typings/create.d.cts +1 -5
- package/typings/create.d.ts +1 -5
- package/typings/enable-if.d.cts +1 -1
- package/typings/enable-if.d.ts +1 -1
- package/typings/index.d.cts +3 -1
- package/typings/index.d.ts +3 -1
- package/typings/orchestrator.d.cts +4 -12
- package/typings/orchestrator.d.ts +4 -12
- package/typings/plugins/use-error-handler.d.cts +3 -2
- package/typings/plugins/use-error-handler.d.ts +3 -2
- package/typings/plugins/use-immediate-introspection.d.cts +6 -0
- package/typings/plugins/use-immediate-introspection.d.ts +6 -0
- package/typings/plugins/use-masked-errors.d.cts +39 -0
- package/typings/plugins/use-masked-errors.d.ts +39 -0
- package/typings/plugins/use-payload-formatter.d.cts +2 -1
- package/typings/plugins/use-payload-formatter.d.ts +2 -1
- package/typings/plugins/use-schema.d.cts +4 -3
- package/typings/plugins/use-schema.d.ts +4 -3
- package/typings/plugins/use-timing.d.cts +21 -0
- package/typings/plugins/use-timing.d.ts +21 -0
- package/typings/traced-orchestrator.d.cts +7 -0
- package/typings/traced-orchestrator.d.ts +7 -0
- package/typings/traced-schema.d.cts +4 -0
- package/typings/traced-schema.d.ts +4 -0
- package/typings/utils.d.cts +8 -4
- package/typings/utils.d.ts +8 -4
package/cjs/utils.js
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.errorAsyncIterator = exports.finalAsyncIterator = exports.handleStreamOrSingleExecutionResult = exports.isAsyncIterable = exports.makeExecute = exports.mapAsyncIterator = exports.makeSubscribe = exports.isIntrospectionOperationString = exports.envelopIsIntrospectionSymbol = void 0;
|
|
3
|
+
exports.errorAsyncIterator = exports.finalAsyncIterator = exports.handleStreamOrSingleExecutionResult = exports.isAsyncIterable = exports.makeExecute = exports.mapAsyncIterator = exports.makeSubscribe = exports.isIntrospectionOperationString = exports.isIntrospectionDocument = exports.isIntrospectionOperation = exports.isOperationDefinition = exports.envelopIsIntrospectionSymbol = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
4
5
|
exports.envelopIsIntrospectionSymbol = Symbol('ENVELOP_IS_INTROSPECTION');
|
|
6
|
+
function isOperationDefinition(def) {
|
|
7
|
+
return def.kind === graphql_1.Kind.OPERATION_DEFINITION;
|
|
8
|
+
}
|
|
9
|
+
exports.isOperationDefinition = isOperationDefinition;
|
|
10
|
+
function isIntrospectionOperation(operation) {
|
|
11
|
+
return isIntrospectionDocument({
|
|
12
|
+
kind: graphql_1.Kind.DOCUMENT,
|
|
13
|
+
definitions: [operation],
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
exports.isIntrospectionOperation = isIntrospectionOperation;
|
|
17
|
+
function isIntrospectionDocument(document) {
|
|
18
|
+
let isIntrospectionOperation = false;
|
|
19
|
+
(0, graphql_1.visit)(document, {
|
|
20
|
+
Field: node => {
|
|
21
|
+
if (node.name.value === '__schema' || node.name.value === '__type') {
|
|
22
|
+
isIntrospectionOperation = true;
|
|
23
|
+
return graphql_1.BREAK;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
return isIntrospectionOperation;
|
|
28
|
+
}
|
|
29
|
+
exports.isIntrospectionDocument = isIntrospectionDocument;
|
|
5
30
|
function isIntrospectionOperationString(operation) {
|
|
6
31
|
return (typeof operation === 'string' ? operation : operation.body).indexOf('__schema') !== -1;
|
|
7
32
|
}
|
package/esm/create.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { isPluginEnabled } from './enable-if.js';
|
|
2
2
|
import { createEnvelopOrchestrator } from './orchestrator.js';
|
|
3
|
+
import { traceOrchestrator } from './traced-orchestrator.js';
|
|
3
4
|
export function envelop(options) {
|
|
4
5
|
const plugins = options.plugins.filter(isPluginEnabled);
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
validate: options.validate,
|
|
10
|
-
subscribe: options.subscribe,
|
|
11
|
-
});
|
|
6
|
+
let orchestrator = createEnvelopOrchestrator(plugins);
|
|
7
|
+
if (options.enableInternalTracing) {
|
|
8
|
+
orchestrator = traceOrchestrator(orchestrator);
|
|
9
|
+
}
|
|
12
10
|
const getEnveloped = (initialContext = {}) => {
|
|
13
11
|
const typedOrchestrator = orchestrator;
|
|
14
12
|
typedOrchestrator.init(initialContext);
|
package/esm/index.js
CHANGED
|
@@ -3,9 +3,11 @@ export * from './create.js';
|
|
|
3
3
|
export * from './utils.js';
|
|
4
4
|
export * from './plugins/use-envelop.js';
|
|
5
5
|
export * from './plugins/use-logger.js';
|
|
6
|
+
export * from './plugins/use-timing.js';
|
|
6
7
|
export * from './plugins/use-schema.js';
|
|
7
8
|
export * from './plugins/use-error-handler.js';
|
|
8
9
|
export * from './plugins/use-extend-context.js';
|
|
9
10
|
export * from './plugins/use-payload-formatter.js';
|
|
11
|
+
export * from './plugins/use-masked-errors.js';
|
|
12
|
+
export * from './plugins/use-immediate-introspection.js';
|
|
10
13
|
export * from './enable-if.js';
|
|
11
|
-
export { resolversHooksSymbol } from './orchestrator.js';
|
package/esm/orchestrator.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { execute, parse, specifiedRules, subscribe, validate, } from 'graphql';
|
|
2
|
+
import { prepareTracedSchema, resolversHooksSymbol } from './traced-schema.js';
|
|
1
3
|
import { errorAsyncIterator, finalAsyncIterator, makeExecute, makeSubscribe, mapAsyncIterator, isAsyncIterable, } from './utils.js';
|
|
2
|
-
export
|
|
3
|
-
export function createEnvelopOrchestrator({ plugins, parse, execute, subscribe, validate, }) {
|
|
4
|
+
export function createEnvelopOrchestrator(plugins) {
|
|
4
5
|
let schema = null;
|
|
5
6
|
let initDone = false;
|
|
6
7
|
const onResolversHandlers = [];
|
|
@@ -14,7 +15,7 @@ export function createEnvelopOrchestrator({ plugins, parse, execute, subscribe,
|
|
|
14
15
|
// here not to call the same plugin that initiated the schema switch.
|
|
15
16
|
const replaceSchema = (newSchema, ignorePluginIndex = -1) => {
|
|
16
17
|
if (onResolversHandlers.length) {
|
|
17
|
-
|
|
18
|
+
prepareTracedSchema(newSchema);
|
|
18
19
|
}
|
|
19
20
|
schema = newSchema;
|
|
20
21
|
if (initDone) {
|
|
@@ -150,10 +151,7 @@ export function createEnvelopOrchestrator({ plugins, parse, execute, subscribe,
|
|
|
150
151
|
validateFn,
|
|
151
152
|
addValidationRule: rule => {
|
|
152
153
|
if (!actualRules) {
|
|
153
|
-
|
|
154
|
-
// eslint-disable-next-line no-console
|
|
155
|
-
console.warn('No default validation rules provided.');
|
|
156
|
-
actualRules = [];
|
|
154
|
+
actualRules = [...specifiedRules];
|
|
157
155
|
}
|
|
158
156
|
actualRules.push(rule);
|
|
159
157
|
},
|
|
@@ -169,9 +167,6 @@ export function createEnvelopOrchestrator({ plugins, parse, execute, subscribe,
|
|
|
169
167
|
if (!result) {
|
|
170
168
|
result = validateFn(schema, documentAST, actualRules, typeInfo, validationOptions);
|
|
171
169
|
}
|
|
172
|
-
if (!result) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
170
|
const valid = result.length === 0;
|
|
176
171
|
for (const afterCb of afterCalls) {
|
|
177
172
|
afterCb({
|
|
@@ -284,9 +279,6 @@ export function createEnvelopOrchestrator({ plugins, parse, execute, subscribe,
|
|
|
284
279
|
// Can be removed once we drop support for GraphQL.js 15
|
|
285
280
|
});
|
|
286
281
|
}
|
|
287
|
-
if (!result) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
282
|
const onNextHandler = [];
|
|
291
283
|
const onEndHandler = [];
|
|
292
284
|
for (const afterCb of afterCalls) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { BREAK } from 'graphql';
|
|
2
|
+
const OnNonIntrospectionFieldReachedValidationRule = (onNonIntrospectionField) => ctx => {
|
|
3
|
+
const rootQueryType = ctx.getSchema().getQueryType();
|
|
4
|
+
const rootMutationType = ctx.getSchema().getMutationType();
|
|
5
|
+
const rootSubscriptionType = ctx.getSchema().getSubscriptionType();
|
|
6
|
+
return {
|
|
7
|
+
Field(field) {
|
|
8
|
+
const parentType = ctx.getParentType();
|
|
9
|
+
const isQuery = parentType === rootQueryType;
|
|
10
|
+
const isMutation = parentType === rootMutationType;
|
|
11
|
+
const isSubscription = parentType === rootSubscriptionType;
|
|
12
|
+
if ((isQuery && !field.name.value.startsWith('__')) || isMutation || isSubscription) {
|
|
13
|
+
onNonIntrospectionField();
|
|
14
|
+
return BREAK;
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const fastIntroSpectionSymbol = Symbol('fastIntrospection');
|
|
21
|
+
/**
|
|
22
|
+
* In case a GraphQL operation only contains introspection fields the context building can be skipped completely.
|
|
23
|
+
* With this plugin any further context extensions will be skipped.
|
|
24
|
+
*/
|
|
25
|
+
export const useImmediateIntrospection = () => {
|
|
26
|
+
return {
|
|
27
|
+
onValidate({ addValidationRule }) {
|
|
28
|
+
let isIntrospectionOnly = true;
|
|
29
|
+
addValidationRule(OnNonIntrospectionFieldReachedValidationRule(() => {
|
|
30
|
+
isIntrospectionOnly = false;
|
|
31
|
+
}));
|
|
32
|
+
return function afterValidate({ extendContext }) {
|
|
33
|
+
if (isIntrospectionOnly) {
|
|
34
|
+
extendContext({ [fastIntroSpectionSymbol]: true });
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
onContextBuilding({ context, breakContextBuilding }) {
|
|
39
|
+
if (context[fastIntroSpectionSymbol]) {
|
|
40
|
+
// hijack and skip all other context related stuff.
|
|
41
|
+
// We dont need a context!
|
|
42
|
+
breakContextBuilding();
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql';
|
|
2
|
+
import { handleStreamOrSingleExecutionResult } from '../utils.js';
|
|
3
|
+
export const DEFAULT_ERROR_MESSAGE = 'Unexpected error.';
|
|
4
|
+
export class EnvelopError extends GraphQLError {
|
|
5
|
+
constructor(message, extensions) {
|
|
6
|
+
super(message, undefined, undefined, undefined, undefined, undefined, extensions);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export const formatError = (err, message, isDev) => {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
if (err instanceof GraphQLError) {
|
|
12
|
+
if (
|
|
13
|
+
/** execution error */
|
|
14
|
+
(err.originalError && err.originalError instanceof EnvelopError === false) ||
|
|
15
|
+
/** validate and parse errors */
|
|
16
|
+
(err.originalError === undefined && err instanceof EnvelopError === false)) {
|
|
17
|
+
return new GraphQLError(message, err.nodes, err.source, err.positions, err.path, undefined, isDev
|
|
18
|
+
? {
|
|
19
|
+
originalError: {
|
|
20
|
+
message: (_b = (_a = err.originalError) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : err.message,
|
|
21
|
+
stack: (_d = (_c = err.originalError) === null || _c === void 0 ? void 0 : _c.stack) !== null && _d !== void 0 ? _d : err.stack,
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
: undefined);
|
|
25
|
+
}
|
|
26
|
+
return err;
|
|
27
|
+
}
|
|
28
|
+
return new GraphQLError(message);
|
|
29
|
+
};
|
|
30
|
+
const makeHandleResult = (format, message, isDev) => ({ result, setResult }) => {
|
|
31
|
+
if (result.errors != null) {
|
|
32
|
+
setResult({ ...result, errors: result.errors.map(error => format(error, message, isDev)) });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export const useMaskedErrors = (opts) => {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
const format = (_a = opts === null || opts === void 0 ? void 0 : opts.formatError) !== null && _a !== void 0 ? _a : formatError;
|
|
38
|
+
const message = (opts === null || opts === void 0 ? void 0 : opts.errorMessage) || DEFAULT_ERROR_MESSAGE;
|
|
39
|
+
// eslint-disable-next-line dot-notation
|
|
40
|
+
const isDev = (_b = opts === null || opts === void 0 ? void 0 : opts.isDev) !== null && _b !== void 0 ? _b : (typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false);
|
|
41
|
+
const handleResult = makeHandleResult(format, message, isDev);
|
|
42
|
+
return {
|
|
43
|
+
onParse: (opts === null || opts === void 0 ? void 0 : opts.handleParseErrors) === true
|
|
44
|
+
? function onParse() {
|
|
45
|
+
return function onParseEnd({ result, replaceParseResult }) {
|
|
46
|
+
if (result instanceof Error) {
|
|
47
|
+
replaceParseResult(format(result, message, isDev));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
: undefined,
|
|
52
|
+
onValidate: (opts === null || opts === void 0 ? void 0 : opts.handleValidationErrors) === true
|
|
53
|
+
? function onValidate() {
|
|
54
|
+
return function onValidateEnd({ valid, result, setResult }) {
|
|
55
|
+
if (valid === false) {
|
|
56
|
+
setResult(result.map(error => format(error, message, isDev)));
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
: undefined,
|
|
61
|
+
onPluginInit(context) {
|
|
62
|
+
context.registerContextErrorHandler(({ error, setError }) => {
|
|
63
|
+
if (error instanceof GraphQLError === false && error instanceof Error) {
|
|
64
|
+
error = new GraphQLError(error.message, undefined, undefined, undefined, undefined, error);
|
|
65
|
+
}
|
|
66
|
+
setError(format(error, message, isDev));
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
onExecute() {
|
|
70
|
+
return {
|
|
71
|
+
onExecuteDone(payload) {
|
|
72
|
+
return handleStreamOrSingleExecutionResult(payload, handleResult);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
onSubscribe() {
|
|
77
|
+
return {
|
|
78
|
+
onSubscribeResult(payload) {
|
|
79
|
+
return handleStreamOrSingleExecutionResult(payload, handleResult);
|
|
80
|
+
},
|
|
81
|
+
onSubscribeError({ error, setError }) {
|
|
82
|
+
setError(format(error, message, isDev));
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { getOperationAST } from 'graphql';
|
|
2
|
+
import { isIntrospectionOperationString, envelopIsIntrospectionSymbol } from '../utils.js';
|
|
3
|
+
const HR_TO_NS = 1e9;
|
|
4
|
+
const NS_TO_MS = 1e6;
|
|
5
|
+
const DEFAULT_OPTIONS = {
|
|
6
|
+
onExecutionMeasurement: (args, timing) => console.log(`Operation execution "${args.operationName}" done in ${timing.ms}ms`),
|
|
7
|
+
onSubscriptionMeasurement: (args, timing) => console.log(`Operation subscription "${args.operationName}" done in ${timing.ms}ms`),
|
|
8
|
+
onParsingMeasurement: (source, timing) => console.log(`Parsing "${source}" done in ${timing.ms}ms`),
|
|
9
|
+
onValidationMeasurement: (document, timing) => { var _a, _b; return console.log(`Validation "${((_b = (_a = getOperationAST(document)) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.value) || '-'}" done in ${timing.ms}ms`); },
|
|
10
|
+
onResolverMeasurement: (info, timing) => console.log(`\tResolver of "${info.parentType.toString()}.${info.fieldName}" done in ${timing.ms}ms`),
|
|
11
|
+
onContextBuildingMeasurement: (timing) => console.log(`Context building done in ${timing.ms}ms`),
|
|
12
|
+
};
|
|
13
|
+
const deltaFrom = (hrtime) => {
|
|
14
|
+
const delta = process.hrtime(hrtime);
|
|
15
|
+
const ns = delta[0] * HR_TO_NS + delta[1];
|
|
16
|
+
return {
|
|
17
|
+
ns,
|
|
18
|
+
get ms() {
|
|
19
|
+
return ns / NS_TO_MS;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export const useTiming = (rawOptions) => {
|
|
24
|
+
const options = {
|
|
25
|
+
...DEFAULT_OPTIONS,
|
|
26
|
+
...rawOptions,
|
|
27
|
+
};
|
|
28
|
+
const result = {};
|
|
29
|
+
if (options.onContextBuildingMeasurement) {
|
|
30
|
+
result.onContextBuilding = ({ context }) => {
|
|
31
|
+
if (context[envelopIsIntrospectionSymbol]) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const contextStartTime = process.hrtime();
|
|
35
|
+
return () => {
|
|
36
|
+
options.onContextBuildingMeasurement(deltaFrom(contextStartTime));
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (options.onParsingMeasurement) {
|
|
41
|
+
result.onParse = ({ params, extendContext }) => {
|
|
42
|
+
if (options.skipIntrospection && isIntrospectionOperationString(params.source)) {
|
|
43
|
+
extendContext({
|
|
44
|
+
[envelopIsIntrospectionSymbol]: true,
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const parseStartTime = process.hrtime();
|
|
49
|
+
return () => {
|
|
50
|
+
options.onParsingMeasurement(params.source, deltaFrom(parseStartTime));
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (options.onValidationMeasurement) {
|
|
55
|
+
result.onValidate = ({ params, context }) => {
|
|
56
|
+
if (context[envelopIsIntrospectionSymbol]) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const validateStartTime = process.hrtime();
|
|
60
|
+
return () => {
|
|
61
|
+
options.onValidationMeasurement(params.documentAST, deltaFrom(validateStartTime));
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (options.onExecutionMeasurement) {
|
|
66
|
+
if (options.onResolverMeasurement) {
|
|
67
|
+
result.onExecute = ({ args }) => {
|
|
68
|
+
if (args.contextValue[envelopIsIntrospectionSymbol]) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const executeStartTime = process.hrtime();
|
|
72
|
+
return {
|
|
73
|
+
onExecuteDone: () => {
|
|
74
|
+
options.onExecutionMeasurement(args, deltaFrom(executeStartTime));
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
result.onResolverCalled = ({ info }) => {
|
|
79
|
+
const resolverStartTime = process.hrtime();
|
|
80
|
+
return () => {
|
|
81
|
+
options.onResolverMeasurement(info, deltaFrom(resolverStartTime));
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
result.onExecute = ({ args }) => {
|
|
87
|
+
if (args.contextValue[envelopIsIntrospectionSymbol]) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const executeStartTime = process.hrtime();
|
|
91
|
+
return {
|
|
92
|
+
onExecuteDone: () => {
|
|
93
|
+
options.onExecutionMeasurement(args, deltaFrom(executeStartTime));
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (options.onSubscriptionMeasurement) {
|
|
100
|
+
if (options.onResolverMeasurement) {
|
|
101
|
+
result.onSubscribe = ({ args }) => {
|
|
102
|
+
if (args.contextValue[envelopIsIntrospectionSymbol]) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const subscribeStartTime = process.hrtime();
|
|
106
|
+
return {
|
|
107
|
+
onSubscribeResult: () => {
|
|
108
|
+
options.onSubscriptionMeasurement && options.onSubscriptionMeasurement(args, deltaFrom(subscribeStartTime));
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
result.onResolverCalled = ({ info }) => {
|
|
113
|
+
const resolverStartTime = process.hrtime();
|
|
114
|
+
return () => {
|
|
115
|
+
options.onResolverMeasurement && options.onResolverMeasurement(info, deltaFrom(resolverStartTime));
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
result.onSubscribe = ({ args }) => {
|
|
121
|
+
if (args.contextValue[envelopIsIntrospectionSymbol]) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const subscribeStartTime = process.hrtime();
|
|
125
|
+
return {
|
|
126
|
+
onSubscribeResult: () => {
|
|
127
|
+
options.onSubscriptionMeasurement && options.onSubscriptionMeasurement(args, deltaFrom(subscribeStartTime));
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
import { GraphQLSchema, } from 'graphql';
|
|
3
|
+
import { isAsyncIterable } from './utils.js';
|
|
4
|
+
const getTimestamp = typeof globalThis !== 'undefined' && ((_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.performance) === null || _a === void 0 ? void 0 : _a.now)
|
|
5
|
+
? () => globalThis.performance.now()
|
|
6
|
+
: () => Date.now();
|
|
7
|
+
const measure = () => {
|
|
8
|
+
const start = getTimestamp();
|
|
9
|
+
return () => {
|
|
10
|
+
const end = getTimestamp();
|
|
11
|
+
return end - start;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const tracingSymbol = Symbol('envelopTracing');
|
|
15
|
+
export function traceOrchestrator(orchestrator) {
|
|
16
|
+
const createTracer = (name, ctx) => {
|
|
17
|
+
const end = measure();
|
|
18
|
+
return () => {
|
|
19
|
+
ctx[tracingSymbol][name] = end();
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
...orchestrator,
|
|
24
|
+
init: (ctx = {}) => {
|
|
25
|
+
ctx[tracingSymbol] = ctx[tracingSymbol] || {};
|
|
26
|
+
const done = createTracer('init', ctx || {});
|
|
27
|
+
try {
|
|
28
|
+
return orchestrator.init(ctx);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
done();
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
parse: (ctx = {}) => {
|
|
35
|
+
ctx[tracingSymbol] = ctx[tracingSymbol] || {};
|
|
36
|
+
const actualFn = orchestrator.parse(ctx);
|
|
37
|
+
return (...args) => {
|
|
38
|
+
const done = createTracer('parse', ctx);
|
|
39
|
+
try {
|
|
40
|
+
return actualFn(...args);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
done();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
validate: (ctx = {}) => {
|
|
48
|
+
ctx[tracingSymbol] = ctx[tracingSymbol] || {};
|
|
49
|
+
const actualFn = orchestrator.validate(ctx);
|
|
50
|
+
return (...args) => {
|
|
51
|
+
const done = createTracer('validate', ctx);
|
|
52
|
+
try {
|
|
53
|
+
return actualFn(...args);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
done();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
execute: async (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver) => {
|
|
61
|
+
const args = argsOrSchema instanceof GraphQLSchema
|
|
62
|
+
? {
|
|
63
|
+
schema: argsOrSchema,
|
|
64
|
+
document: document,
|
|
65
|
+
rootValue,
|
|
66
|
+
contextValue,
|
|
67
|
+
variableValues,
|
|
68
|
+
operationName,
|
|
69
|
+
fieldResolver,
|
|
70
|
+
typeResolver,
|
|
71
|
+
}
|
|
72
|
+
: argsOrSchema;
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
74
|
+
// @ts-ignore GraphQL.js types contextValue as unknown
|
|
75
|
+
const done = createTracer('execute', args.contextValue || {});
|
|
76
|
+
try {
|
|
77
|
+
const result = await orchestrator.execute(args);
|
|
78
|
+
done();
|
|
79
|
+
if (!isAsyncIterable(result)) {
|
|
80
|
+
result.extensions = result.extensions || {};
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
82
|
+
// @ts-ignore GraphQL.js types contextValue as unknown
|
|
83
|
+
result.extensions.envelopTracing = args.contextValue[tracingSymbol];
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// eslint-disable-next-line no-console
|
|
87
|
+
console.warn(`"traceOrchestrator" encountered a AsyncIterator which is not supported yet, so tracing data is not available for the operation.`);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
done();
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
subscribe: async (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, subscribeFieldResolver) => {
|
|
97
|
+
const args = argsOrSchema instanceof GraphQLSchema
|
|
98
|
+
? {
|
|
99
|
+
schema: argsOrSchema,
|
|
100
|
+
document: document,
|
|
101
|
+
rootValue,
|
|
102
|
+
contextValue,
|
|
103
|
+
variableValues,
|
|
104
|
+
operationName,
|
|
105
|
+
fieldResolver,
|
|
106
|
+
subscribeFieldResolver,
|
|
107
|
+
}
|
|
108
|
+
: argsOrSchema;
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
110
|
+
// @ts-ignore GraphQL.js types contextValue as unknown
|
|
111
|
+
const done = createTracer('subscribe', args.contextValue || {});
|
|
112
|
+
try {
|
|
113
|
+
return await orchestrator.subscribe(args);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
done();
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
contextFactory: (ctx = {}) => {
|
|
120
|
+
const actualFn = orchestrator.contextFactory(ctx);
|
|
121
|
+
return async (childCtx) => {
|
|
122
|
+
const done = createTracer('contextFactory', ctx);
|
|
123
|
+
try {
|
|
124
|
+
return await actualFn(childCtx);
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
done();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { defaultFieldResolver, isIntrospectionType, isObjectType } from 'graphql';
|
|
2
|
+
export const trackedSchemaSymbol = Symbol('TRACKED_SCHEMA');
|
|
3
|
+
export const resolversHooksSymbol = Symbol('RESOLVERS_HOOKS');
|
|
4
|
+
export function prepareTracedSchema(schema) {
|
|
5
|
+
if (!schema || schema[trackedSchemaSymbol]) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
schema[trackedSchemaSymbol] = true;
|
|
9
|
+
const entries = Object.values(schema.getTypeMap());
|
|
10
|
+
for (const type of entries) {
|
|
11
|
+
if (!isIntrospectionType(type) && isObjectType(type)) {
|
|
12
|
+
const fields = Object.values(type.getFields());
|
|
13
|
+
for (const field of fields) {
|
|
14
|
+
let resolverFn = (field.resolve || defaultFieldResolver);
|
|
15
|
+
field.resolve = async (root, args, context, info) => {
|
|
16
|
+
if (context && context[resolversHooksSymbol]) {
|
|
17
|
+
const hooks = context[resolversHooksSymbol];
|
|
18
|
+
const afterCalls = [];
|
|
19
|
+
for (const hook of hooks) {
|
|
20
|
+
const afterFn = await hook({
|
|
21
|
+
root,
|
|
22
|
+
args,
|
|
23
|
+
context,
|
|
24
|
+
info,
|
|
25
|
+
resolverFn,
|
|
26
|
+
replaceResolverFn: newFn => {
|
|
27
|
+
resolverFn = newFn;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
afterFn && afterCalls.push(afterFn);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
let result = await resolverFn(root, args, context, info);
|
|
34
|
+
for (const afterFn of afterCalls) {
|
|
35
|
+
afterFn({
|
|
36
|
+
result,
|
|
37
|
+
setResult: newResult => {
|
|
38
|
+
result = newResult;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
let resultErr = e;
|
|
46
|
+
for (const afterFn of afterCalls) {
|
|
47
|
+
afterFn({
|
|
48
|
+
result: resultErr,
|
|
49
|
+
setResult: newResult => {
|
|
50
|
+
resultErr = newResult;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
throw resultErr;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return resolverFn(root, args, context, info);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
package/esm/utils.js
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
|
+
import { Kind, visit, BREAK, } from 'graphql';
|
|
1
2
|
export const envelopIsIntrospectionSymbol = Symbol('ENVELOP_IS_INTROSPECTION');
|
|
3
|
+
export function isOperationDefinition(def) {
|
|
4
|
+
return def.kind === Kind.OPERATION_DEFINITION;
|
|
5
|
+
}
|
|
6
|
+
export function isIntrospectionOperation(operation) {
|
|
7
|
+
return isIntrospectionDocument({
|
|
8
|
+
kind: Kind.DOCUMENT,
|
|
9
|
+
definitions: [operation],
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function isIntrospectionDocument(document) {
|
|
13
|
+
let isIntrospectionOperation = false;
|
|
14
|
+
visit(document, {
|
|
15
|
+
Field: node => {
|
|
16
|
+
if (node.name.value === '__schema' || node.name.value === '__type') {
|
|
17
|
+
isIntrospectionOperation = true;
|
|
18
|
+
return BREAK;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
return isIntrospectionOperation;
|
|
23
|
+
}
|
|
2
24
|
export function isIntrospectionOperationString(operation) {
|
|
3
25
|
return (typeof operation === 'string' ? operation : operation.body).indexOf('__schema') !== -1;
|
|
4
26
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
7
|
+
},
|
|
5
8
|
"dependencies": {
|
|
6
|
-
"@envelop/types": "2.
|
|
9
|
+
"@envelop/types": "2.4.0",
|
|
10
|
+
"tslib": "2.4.0"
|
|
7
11
|
},
|
|
8
12
|
"repository": {
|
|
9
13
|
"type": "git",
|
package/typings/create.d.cts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { GetEnvelopedFn, ComposeContext, Plugin
|
|
1
|
+
import { GetEnvelopedFn, ComposeContext, Plugin } from '@envelop/types';
|
|
2
2
|
import { PluginOrDisabledPlugin } from './enable-if.cjs';
|
|
3
3
|
export declare function envelop<PluginsType extends Plugin<any>[]>(options: {
|
|
4
4
|
plugins: Array<PluginOrDisabledPlugin>;
|
|
5
5
|
enableInternalTracing?: boolean;
|
|
6
|
-
parse: ParseFunction;
|
|
7
|
-
execute: ExecuteFunction;
|
|
8
|
-
validate: ValidateFunction;
|
|
9
|
-
subscribe: SubscribeFunction;
|
|
10
6
|
}): GetEnvelopedFn<ComposeContext<PluginsType>>;
|
package/typings/create.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { GetEnvelopedFn, ComposeContext, Plugin
|
|
1
|
+
import { GetEnvelopedFn, ComposeContext, Plugin } from '@envelop/types';
|
|
2
2
|
import { PluginOrDisabledPlugin } from './enable-if.js';
|
|
3
3
|
export declare function envelop<PluginsType extends Plugin<any>[]>(options: {
|
|
4
4
|
plugins: Array<PluginOrDisabledPlugin>;
|
|
5
5
|
enableInternalTracing?: boolean;
|
|
6
|
-
parse: ParseFunction;
|
|
7
|
-
execute: ExecuteFunction;
|
|
8
|
-
validate: ValidateFunction;
|
|
9
|
-
subscribe: SubscribeFunction;
|
|
10
6
|
}): GetEnvelopedFn<ComposeContext<PluginsType>>;
|
package/typings/enable-if.d.cts
CHANGED
|
@@ -10,5 +10,5 @@ export declare function isPluginEnabled(t: PluginOrDisabledPlugin): t is Plugin;
|
|
|
10
10
|
/**
|
|
11
11
|
* Utility function to enable a plugin.
|
|
12
12
|
*/
|
|
13
|
-
export declare function enableIf<PluginContextType = {}>(condition: boolean, plugin: Plugin<PluginContextType> | (() => Plugin<PluginContextType>)): PluginOrDisabledPlugin;
|
|
13
|
+
export declare function enableIf<PluginContextType extends Record<any, any> = {}>(condition: boolean, plugin: Plugin<PluginContextType> | (() => Plugin<PluginContextType>)): PluginOrDisabledPlugin;
|
|
14
14
|
export {};
|
package/typings/enable-if.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export declare function isPluginEnabled(t: PluginOrDisabledPlugin): t is Plugin;
|
|
|
10
10
|
/**
|
|
11
11
|
* Utility function to enable a plugin.
|
|
12
12
|
*/
|
|
13
|
-
export declare function enableIf<PluginContextType = {}>(condition: boolean, plugin: Plugin<PluginContextType> | (() => Plugin<PluginContextType>)): PluginOrDisabledPlugin;
|
|
13
|
+
export declare function enableIf<PluginContextType extends Record<any, any> = {}>(condition: boolean, plugin: Plugin<PluginContextType> | (() => Plugin<PluginContextType>)): PluginOrDisabledPlugin;
|
|
14
14
|
export {};
|