@envelop/on-resolve 2.0.0-alpha-20220907123006-46eb16d1
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 +98 -0
- package/cjs/index.js +57 -0
- package/cjs/package.json +1 -0
- package/esm/index.js +53 -0
- package/package.json +55 -0
- package/typings/index.d.cts +22 -0
- package/typings/index.d.ts +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
## `@envelop/on-resolve`
|
|
2
|
+
|
|
3
|
+
This plugin allows you to hook into resolves of every field in the GraphQL schema.
|
|
4
|
+
|
|
5
|
+
Useful for tracing or augmenting resolvers (and their results) with custom logic.
|
|
6
|
+
|
|
7
|
+
## Getting Started
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
yarn add @envelop/on-resolve
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage Example
|
|
14
|
+
|
|
15
|
+
### Custom field resolutions
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { envelop } from '@envelop/core'
|
|
19
|
+
import { useOnResolve } from '@envelop/on-resolve'
|
|
20
|
+
import { specialResolver } from './my-resolvers'
|
|
21
|
+
|
|
22
|
+
const getEnveloped = envelop({
|
|
23
|
+
plugins: [
|
|
24
|
+
// ... other plugins ...
|
|
25
|
+
useOnResolve(async function onResolve({ context, root, args, info, replaceResolver }) {
|
|
26
|
+
// replace special field's resolver
|
|
27
|
+
if (info.fieldName === 'special') {
|
|
28
|
+
replaceResolver(specialResolver)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// replace field's result
|
|
32
|
+
if (info.fieldName === 'alwaysHello') {
|
|
33
|
+
return ({ setResult }) => {
|
|
34
|
+
setResult('hello')
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Tracing
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { envelop, Plugin } from '@envelop/core'
|
|
46
|
+
import { useOnResolve } from '@envelop/on-resolve'
|
|
47
|
+
|
|
48
|
+
interface FieldTracingPluginContext {
|
|
49
|
+
tracerUrl: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function useFieldTracing() {
|
|
53
|
+
return {
|
|
54
|
+
onPluginInit({ addPlugin }) {
|
|
55
|
+
addPlugin(
|
|
56
|
+
useOnResolve(async function onResolve({ context, root, args, info }) {
|
|
57
|
+
await fetch(context.tracerUrl, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'content-type': 'application/json'
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
startedResolving: {
|
|
64
|
+
...info,
|
|
65
|
+
parent: root,
|
|
66
|
+
args
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
return async () => {
|
|
72
|
+
await fetch(context.tracerUrl, {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: {
|
|
75
|
+
'content-type': 'application/json'
|
|
76
|
+
},
|
|
77
|
+
body: JSON.stringify({
|
|
78
|
+
endedResolving: {
|
|
79
|
+
...info,
|
|
80
|
+
parent: root,
|
|
81
|
+
args
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const getEnveloped = envelop({
|
|
93
|
+
plugins: [
|
|
94
|
+
// ... other plugins ...
|
|
95
|
+
useSpecialResolve()
|
|
96
|
+
]
|
|
97
|
+
})
|
|
98
|
+
```
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useOnResolve = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
/**
|
|
6
|
+
* Wraps the provided schema by hooking into the resolvers of every field.
|
|
7
|
+
*
|
|
8
|
+
* Use the `onResolve` argument to manipulate the resolver and its results/errors.
|
|
9
|
+
*/
|
|
10
|
+
function useOnResolve(onResolve) {
|
|
11
|
+
return {
|
|
12
|
+
onSchemaChange({ schema: _schema }) {
|
|
13
|
+
const schema = _schema;
|
|
14
|
+
if (!schema)
|
|
15
|
+
return; // nothing to do if schema is missing
|
|
16
|
+
for (const type of Object.values(schema.getTypeMap())) {
|
|
17
|
+
if (!(0, graphql_1.isIntrospectionType)(type) && (0, graphql_1.isObjectType)(type)) {
|
|
18
|
+
for (const field of Object.values(type.getFields())) {
|
|
19
|
+
let resolver = (field.resolve || graphql_1.defaultFieldResolver);
|
|
20
|
+
field.resolve = async (root, args, context, info) => {
|
|
21
|
+
const afterResolve = await onResolve({
|
|
22
|
+
root,
|
|
23
|
+
args,
|
|
24
|
+
context,
|
|
25
|
+
info,
|
|
26
|
+
resolver,
|
|
27
|
+
replaceResolver: newResolver => {
|
|
28
|
+
resolver = newResolver;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
let result;
|
|
32
|
+
try {
|
|
33
|
+
result = await resolver(root, args, context, info);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
result = err;
|
|
37
|
+
}
|
|
38
|
+
if (typeof afterResolve === 'function') {
|
|
39
|
+
await afterResolve({
|
|
40
|
+
result,
|
|
41
|
+
setResult: newResult => {
|
|
42
|
+
result = newResult;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (result instanceof Error) {
|
|
47
|
+
throw result;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
exports.useOnResolve = useOnResolve;
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defaultFieldResolver, isIntrospectionType, isObjectType } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps the provided schema by hooking into the resolvers of every field.
|
|
4
|
+
*
|
|
5
|
+
* Use the `onResolve` argument to manipulate the resolver and its results/errors.
|
|
6
|
+
*/
|
|
7
|
+
export function useOnResolve(onResolve) {
|
|
8
|
+
return {
|
|
9
|
+
onSchemaChange({ schema: _schema }) {
|
|
10
|
+
const schema = _schema;
|
|
11
|
+
if (!schema)
|
|
12
|
+
return; // nothing to do if schema is missing
|
|
13
|
+
for (const type of Object.values(schema.getTypeMap())) {
|
|
14
|
+
if (!isIntrospectionType(type) && isObjectType(type)) {
|
|
15
|
+
for (const field of Object.values(type.getFields())) {
|
|
16
|
+
let resolver = (field.resolve || defaultFieldResolver);
|
|
17
|
+
field.resolve = async (root, args, context, info) => {
|
|
18
|
+
const afterResolve = await onResolve({
|
|
19
|
+
root,
|
|
20
|
+
args,
|
|
21
|
+
context,
|
|
22
|
+
info,
|
|
23
|
+
resolver,
|
|
24
|
+
replaceResolver: newResolver => {
|
|
25
|
+
resolver = newResolver;
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
let result;
|
|
29
|
+
try {
|
|
30
|
+
result = await resolver(root, args, context, info);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
result = err;
|
|
34
|
+
}
|
|
35
|
+
if (typeof afterResolve === 'function') {
|
|
36
|
+
await afterResolve({
|
|
37
|
+
result,
|
|
38
|
+
setResult: newResult => {
|
|
39
|
+
result = newResult;
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (result instanceof Error) {
|
|
44
|
+
throw result;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@envelop/on-resolve",
|
|
3
|
+
"version": "2.0.0-alpha-20220907123006-46eb16d1",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"@envelop/core": "3.0.0-alpha-20220907123006-46eb16d1",
|
|
7
|
+
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/n1ru4l/envelop.git",
|
|
13
|
+
"directory": "packages/plugins/on-resolve"
|
|
14
|
+
},
|
|
15
|
+
"author": "Denis Badurina <badurinadenis@gmail.com>",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"main": "cjs/index.js",
|
|
18
|
+
"module": "esm/index.js",
|
|
19
|
+
"typings": "typings/index.d.ts",
|
|
20
|
+
"typescript": {
|
|
21
|
+
"definition": "typings/index.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./typings/index.d.cts",
|
|
28
|
+
"default": "./cjs/index.js"
|
|
29
|
+
},
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./typings/index.d.ts",
|
|
32
|
+
"default": "./esm/index.js"
|
|
33
|
+
},
|
|
34
|
+
"default": {
|
|
35
|
+
"types": "./typings/index.d.ts",
|
|
36
|
+
"default": "./esm/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./*": {
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./typings/*.d.cts",
|
|
42
|
+
"default": "./cjs/*.js"
|
|
43
|
+
},
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./typings/*.d.ts",
|
|
46
|
+
"default": "./esm/*.js"
|
|
47
|
+
},
|
|
48
|
+
"default": {
|
|
49
|
+
"types": "./typings/*.d.ts",
|
|
50
|
+
"default": "./esm/*.js"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"./package.json": "./package.json"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
2
|
+
import { Plugin, PromiseOrValue } from '@envelop/core';
|
|
3
|
+
export declare type Resolver<Context = unknown> = (root: unknown, args: Record<string, unknown>, context: Context, info: GraphQLResolveInfo) => PromiseOrValue<unknown>;
|
|
4
|
+
export declare type AfterResolver = (options: {
|
|
5
|
+
result: unknown;
|
|
6
|
+
setResult: (newResult: unknown) => void;
|
|
7
|
+
}) => PromiseOrValue<void>;
|
|
8
|
+
export interface OnResolveOptions<PluginContext extends Record<string, any> = {}> {
|
|
9
|
+
context: PluginContext;
|
|
10
|
+
root: unknown;
|
|
11
|
+
args: Record<string, unknown>;
|
|
12
|
+
info: GraphQLResolveInfo;
|
|
13
|
+
resolver: Resolver<PluginContext>;
|
|
14
|
+
replaceResolver: (newResolver: Resolver<PluginContext>) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare type OnResolve<PluginContext extends Record<string, any> = {}> = (options: OnResolveOptions<PluginContext>) => PromiseOrValue<AfterResolver | void>;
|
|
17
|
+
/**
|
|
18
|
+
* Wraps the provided schema by hooking into the resolvers of every field.
|
|
19
|
+
*
|
|
20
|
+
* Use the `onResolve` argument to manipulate the resolver and its results/errors.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useOnResolve<PluginContext extends Record<string, any> = {}>(onResolve: OnResolve<PluginContext>): Plugin<PluginContext>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
2
|
+
import { Plugin, PromiseOrValue } from '@envelop/core';
|
|
3
|
+
export declare type Resolver<Context = unknown> = (root: unknown, args: Record<string, unknown>, context: Context, info: GraphQLResolveInfo) => PromiseOrValue<unknown>;
|
|
4
|
+
export declare type AfterResolver = (options: {
|
|
5
|
+
result: unknown;
|
|
6
|
+
setResult: (newResult: unknown) => void;
|
|
7
|
+
}) => PromiseOrValue<void>;
|
|
8
|
+
export interface OnResolveOptions<PluginContext extends Record<string, any> = {}> {
|
|
9
|
+
context: PluginContext;
|
|
10
|
+
root: unknown;
|
|
11
|
+
args: Record<string, unknown>;
|
|
12
|
+
info: GraphQLResolveInfo;
|
|
13
|
+
resolver: Resolver<PluginContext>;
|
|
14
|
+
replaceResolver: (newResolver: Resolver<PluginContext>) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare type OnResolve<PluginContext extends Record<string, any> = {}> = (options: OnResolveOptions<PluginContext>) => PromiseOrValue<AfterResolver | void>;
|
|
17
|
+
/**
|
|
18
|
+
* Wraps the provided schema by hooking into the resolvers of every field.
|
|
19
|
+
*
|
|
20
|
+
* Use the `onResolve` argument to manipulate the resolver and its results/errors.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useOnResolve<PluginContext extends Record<string, any> = {}>(onResolve: OnResolve<PluginContext>): Plugin<PluginContext>;
|