@envelop/persisted-operations 1.0.0 → 2.1.0-alpha-e8998c9.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/README.md +69 -20
- package/index.d.ts +6 -40
- package/index.js +92 -28
- package/index.mjs +89 -28
- package/package.json +2 -2
- package/plugin.d.ts +25 -0
- package/stores/aggregated-store.d.ts +7 -0
- package/stores/inmemory-store.d.ts +12 -0
- package/stores/json-file-store.d.ts +9 -0
- package/types.d.ts +12 -0
- package/utils.d.ts +2 -0
- package/index.js.map +0 -1
- package/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -12,50 +12,99 @@ yarn add @envelop/persisted-operations
|
|
|
12
12
|
|
|
13
13
|
## Usage Example
|
|
14
14
|
|
|
15
|
+
The most basic implementation can use an in-memory JS `Map` wrapper with a `Store` object:
|
|
16
|
+
|
|
15
17
|
```ts
|
|
16
18
|
import { envelop } from '@envelop/core';
|
|
17
|
-
import { usePersistedOperations } from '@envelop/persisted-operations';
|
|
19
|
+
import { usePersistedOperations, InMemoryStore } from '@envelop/persisted-operations';
|
|
20
|
+
|
|
21
|
+
// You can retrieve the store in any way (e.g. from a remote source) and implement it with a simple Map / Key->Value
|
|
22
|
+
const myData = new Map();
|
|
23
|
+
myData.set('persisted_1', parse(`query { ... }`));
|
|
24
|
+
myData.set('persisted_2', 'query { ... }'));
|
|
25
|
+
|
|
26
|
+
const store = new InMemoryStore({
|
|
27
|
+
initialData: myData,
|
|
28
|
+
})
|
|
18
29
|
|
|
19
30
|
const getEnveloped = envelop({
|
|
20
31
|
plugins: [
|
|
21
32
|
// ... other plugins ...
|
|
22
33
|
usePersistedOperations({
|
|
23
34
|
store: myStore,
|
|
24
|
-
onlyPersistedOperations: true, // Change to false, to allow regular operations in addition
|
|
25
35
|
}),
|
|
26
36
|
],
|
|
27
37
|
});
|
|
28
38
|
```
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
Now, when running operations through your GraphQL server, you can use a key instead of a query language:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"query": "persisted_1",
|
|
45
|
+
"variables": {}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
31
48
|
|
|
32
|
-
|
|
49
|
+
You can also provide a function to retrieve the operation id from a custom property available in your context / incoming request:
|
|
33
50
|
|
|
34
51
|
```ts
|
|
35
|
-
|
|
52
|
+
usePersistedOperations({
|
|
53
|
+
store: myStore,
|
|
54
|
+
extractOperationId: (context) => context.request.body.operationId // get id from custom property in body object
|
|
55
|
+
}),
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Usage Example with built-in JsonFileStore
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { usePersistedOperations, JsonFileStore } from '@envelop/persisted-operations';
|
|
62
|
+
|
|
63
|
+
const persistedOperationsStore = new JsonFilesStore();
|
|
64
|
+
const filePath = resolve(process.cwd(), 'assets/client1PersistedOperations.json');
|
|
36
65
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
persisted_1: parse(`query`),
|
|
40
|
-
};
|
|
66
|
+
// sync
|
|
67
|
+
persistedOperationsStore.loadFromFileSync(filePath); // load and parse persisted-operations files
|
|
41
68
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
// or async
|
|
70
|
+
await persistedOperationsStore.loadFromFile(filePath); // load and parse persisted-operations files
|
|
71
|
+
|
|
72
|
+
const getEnveloped = envelop({
|
|
73
|
+
plugins: [
|
|
74
|
+
// ... other plugins ...
|
|
75
|
+
usePersistedOperations({
|
|
76
|
+
store: persistedOperationsStore,
|
|
77
|
+
}),
|
|
78
|
+
],
|
|
79
|
+
});
|
|
46
80
|
```
|
|
47
81
|
|
|
48
|
-
|
|
82
|
+
## Multiple Stores
|
|
49
83
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
84
|
+
The `store` parameter accepts both a `Store` instance, or a function. If you need to support multiple stores (based on incoming GraphQL operation/HTTP request), you can provide a function to toggle between the stores, based on your needs:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const getEnveloped = envelop({
|
|
88
|
+
plugins: [
|
|
89
|
+
// ... other plugins ...
|
|
90
|
+
usePersistedOperations({
|
|
91
|
+
store: context => {
|
|
92
|
+
if (context.req.headers['user-agent'].includes('Android')) {
|
|
93
|
+
return mobileClientsStore;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return defaultStore;
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// later, pass the initial context
|
|
103
|
+
const proxyFns = getEnveloped({ req });
|
|
55
104
|
```
|
|
56
105
|
|
|
57
106
|
## With Relay
|
|
58
107
|
|
|
59
108
|
If you are using Relay, you can leverage `relay-compiler` feature for hashing and and creating the store for you, during build.
|
|
60
109
|
|
|
61
|
-
You can [read more about this feature here](https://relay.dev/docs/
|
|
110
|
+
You can [read more about this feature here](https://relay.dev/docs/guides/persisted-queries/). After building your hashes, you can use `queryMap.json` as your store.
|
package/index.d.ts
CHANGED
|
@@ -1,40 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
* Check if the provided string content can be handled by the store or not.
|
|
8
|
-
* @param idOrSdl persisted id/hash, or SDL, provided for `parse`
|
|
9
|
-
*/
|
|
10
|
-
canHandle(idOrSdl: string): boolean;
|
|
11
|
-
/**
|
|
12
|
-
* Transfroms a hash/id into a DocumentNode or SDL string.
|
|
13
|
-
* Prefer returning `DocumentNode` if available to avoid additional parsing phase.
|
|
14
|
-
* Return `null` in case of a store miss.
|
|
15
|
-
* @param id
|
|
16
|
-
*/
|
|
17
|
-
get(id: string): string | DocumentNode | null;
|
|
18
|
-
}
|
|
19
|
-
export declare type UsePersistedOperationsOptions = {
|
|
20
|
-
/**
|
|
21
|
-
* Set to `false` to allow running operations that are not available in the store.
|
|
22
|
-
* Set to `true` to allow running only persisted operations.
|
|
23
|
-
*/
|
|
24
|
-
onlyPersistedOperations: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* The store to use. You can implement any custom store that loads the data from any source.
|
|
27
|
-
*/
|
|
28
|
-
store: PersistedOperationsStore;
|
|
29
|
-
/**
|
|
30
|
-
* Writes operation id to the context (enabled by default)
|
|
31
|
-
*/
|
|
32
|
-
writeToContext?: boolean;
|
|
33
|
-
};
|
|
34
|
-
declare const symbolInContext: unique symbol;
|
|
35
|
-
declare type PluginContext<TOptions extends Partial<UsePersistedOperationsOptions>> = TOptions['writeToContext'] extends true ? {
|
|
36
|
-
[symbolInContext]: string;
|
|
37
|
-
} : {};
|
|
38
|
-
export declare function readOperationId<TOptions extends UsePersistedOperationsOptions>(context: PluginContext<TOptions>): string;
|
|
39
|
-
export declare const usePersistedOperations: <TOptions extends UsePersistedOperationsOptions>(options: TOptions) => Plugin<PluginContext<TOptions>>;
|
|
40
|
-
export {};
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './utils';
|
|
3
|
+
export * from './stores/aggregated-store';
|
|
4
|
+
export * from './stores/inmemory-store';
|
|
5
|
+
export * from './stores/json-file-store';
|
|
6
|
+
export * from './plugin';
|
package/index.js
CHANGED
|
@@ -2,45 +2,109 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
const fs = require('fs');
|
|
5
6
|
const graphql = require('graphql');
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
function operationIdFromSource(source) {
|
|
9
|
+
return typeof source === 'string' && source.length && source.indexOf('{') === -1 ? source : undefined;
|
|
8
10
|
}
|
|
9
|
-
|
|
11
|
+
|
|
12
|
+
class AggregatedStore {
|
|
13
|
+
constructor(stores) {
|
|
14
|
+
this.stores = stores;
|
|
15
|
+
}
|
|
16
|
+
get(operationId) {
|
|
17
|
+
for (const store of this.stores) {
|
|
18
|
+
const item = store.get(operationId);
|
|
19
|
+
if (item) {
|
|
20
|
+
return item;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class InMemoryStore {
|
|
28
|
+
constructor(options) {
|
|
29
|
+
var _a;
|
|
30
|
+
this.storeData = (_a = options === null || options === void 0 ? void 0 : options.initialData) !== null && _a !== void 0 ? _a : new Map();
|
|
31
|
+
}
|
|
32
|
+
get(operationId) {
|
|
33
|
+
return this.storeData.get(operationId) || undefined;
|
|
34
|
+
}
|
|
35
|
+
prime(operationId, document) {
|
|
36
|
+
this.storeData.set(operationId, document);
|
|
37
|
+
}
|
|
38
|
+
clear(operationId) {
|
|
39
|
+
this.storeData.delete(operationId);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class JsonFileStore {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.storeData = null;
|
|
46
|
+
}
|
|
47
|
+
get(operationId) {
|
|
48
|
+
if (!this.storeData) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return this.storeData.get(operationId) || undefined;
|
|
52
|
+
}
|
|
53
|
+
loadFromFileSync(path) {
|
|
54
|
+
const data = JSON.parse(fs.readFileSync(path, 'utf-8'));
|
|
55
|
+
this.storeData = new Map(Object.entries(data));
|
|
56
|
+
}
|
|
57
|
+
async loadFromFile(path) {
|
|
58
|
+
const data = JSON.parse(await fs.promises.readFile(path, 'utf-8'));
|
|
59
|
+
this.storeData = new Map(Object.entries(data));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const DEFAULT_OPTIONS = {
|
|
64
|
+
onlyPersisted: false,
|
|
65
|
+
};
|
|
66
|
+
const contextProperty = Symbol('persistedOperationId');
|
|
10
67
|
function readOperationId(context) {
|
|
11
|
-
return context[
|
|
68
|
+
return context[contextProperty];
|
|
12
69
|
}
|
|
13
|
-
const usePersistedOperations = (
|
|
14
|
-
const
|
|
70
|
+
const usePersistedOperations = (rawOptions) => {
|
|
71
|
+
const options = {
|
|
72
|
+
...DEFAULT_OPTIONS,
|
|
73
|
+
...(rawOptions || {}),
|
|
74
|
+
};
|
|
15
75
|
return {
|
|
16
|
-
onParse({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const result = typeof rawResult === 'string' ? graphql.parse(rawResult) : rawResult;
|
|
22
|
-
if (writeToContext) {
|
|
23
|
-
extendContext({
|
|
24
|
-
[symbolInContext]: params.source,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
setParsedDocument(result);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
throw new NonPersistedOperationError(`The operation hash "${params.source}" is not valid`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
if (options.onlyPersistedOperations) {
|
|
35
|
-
throw new graphql.GraphQLError(`Failed to handle GraphQL persisted operation.`);
|
|
36
|
-
}
|
|
76
|
+
onParse({ context, params, extendContext, setParsedDocument }) {
|
|
77
|
+
const operationId = options.extractOperationId ? options.extractOperationId(context) : operationIdFromSource(params.source);
|
|
78
|
+
if (!operationId) {
|
|
79
|
+
if (options.onlyPersisted) {
|
|
80
|
+
throw new graphql.GraphQLError('Must provide operation id');
|
|
37
81
|
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const store = typeof options.store === 'function' ? options.store(context) : options.store;
|
|
85
|
+
if (!store) {
|
|
86
|
+
throw new graphql.GraphQLError('Must provide store for persisted-operations!');
|
|
87
|
+
}
|
|
88
|
+
const rawResult = store.get(operationId);
|
|
89
|
+
if (rawResult) {
|
|
90
|
+
const document = typeof rawResult === 'string' ? graphql.parse(rawResult) : rawResult;
|
|
91
|
+
extendContext({ [contextProperty]: operationId });
|
|
92
|
+
setParsedDocument(document);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (options.onlyPersisted) {
|
|
96
|
+
// we want to throw an error only when "onlyPersisted" is true, otherwise we let execution continue normally
|
|
97
|
+
throw new graphql.GraphQLError(`Unable to match operation with id '${operationId}'`);
|
|
38
98
|
}
|
|
99
|
+
// if we reach this stage we could not retrieve a persisted operation and we didn't throw any error as onlyPersisted is false
|
|
100
|
+
// hence we let operation continue assuming consumer is not passing an operation id, but a plain query string, with current request.
|
|
39
101
|
},
|
|
40
102
|
};
|
|
41
103
|
};
|
|
42
104
|
|
|
43
|
-
exports.
|
|
105
|
+
exports.AggregatedStore = AggregatedStore;
|
|
106
|
+
exports.InMemoryStore = InMemoryStore;
|
|
107
|
+
exports.JsonFileStore = JsonFileStore;
|
|
108
|
+
exports.operationIdFromSource = operationIdFromSource;
|
|
44
109
|
exports.readOperationId = readOperationId;
|
|
45
110
|
exports.usePersistedOperations = usePersistedOperations;
|
|
46
|
-
//# sourceMappingURL=index.js.map
|
package/index.mjs
CHANGED
|
@@ -1,40 +1,101 @@
|
|
|
1
|
+
import { readFileSync, promises } from 'fs';
|
|
1
2
|
import { GraphQLError, parse } from 'graphql';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function operationIdFromSource(source) {
|
|
5
|
+
return typeof source === 'string' && source.length && source.indexOf('{') === -1 ? source : undefined;
|
|
4
6
|
}
|
|
5
|
-
|
|
7
|
+
|
|
8
|
+
class AggregatedStore {
|
|
9
|
+
constructor(stores) {
|
|
10
|
+
this.stores = stores;
|
|
11
|
+
}
|
|
12
|
+
get(operationId) {
|
|
13
|
+
for (const store of this.stores) {
|
|
14
|
+
const item = store.get(operationId);
|
|
15
|
+
if (item) {
|
|
16
|
+
return item;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class InMemoryStore {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
var _a;
|
|
26
|
+
this.storeData = (_a = options === null || options === void 0 ? void 0 : options.initialData) !== null && _a !== void 0 ? _a : new Map();
|
|
27
|
+
}
|
|
28
|
+
get(operationId) {
|
|
29
|
+
return this.storeData.get(operationId) || undefined;
|
|
30
|
+
}
|
|
31
|
+
prime(operationId, document) {
|
|
32
|
+
this.storeData.set(operationId, document);
|
|
33
|
+
}
|
|
34
|
+
clear(operationId) {
|
|
35
|
+
this.storeData.delete(operationId);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class JsonFileStore {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.storeData = null;
|
|
42
|
+
}
|
|
43
|
+
get(operationId) {
|
|
44
|
+
if (!this.storeData) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return this.storeData.get(operationId) || undefined;
|
|
48
|
+
}
|
|
49
|
+
loadFromFileSync(path) {
|
|
50
|
+
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
51
|
+
this.storeData = new Map(Object.entries(data));
|
|
52
|
+
}
|
|
53
|
+
async loadFromFile(path) {
|
|
54
|
+
const data = JSON.parse(await promises.readFile(path, 'utf-8'));
|
|
55
|
+
this.storeData = new Map(Object.entries(data));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const DEFAULT_OPTIONS = {
|
|
60
|
+
onlyPersisted: false,
|
|
61
|
+
};
|
|
62
|
+
const contextProperty = Symbol('persistedOperationId');
|
|
6
63
|
function readOperationId(context) {
|
|
7
|
-
return context[
|
|
64
|
+
return context[contextProperty];
|
|
8
65
|
}
|
|
9
|
-
const usePersistedOperations = (
|
|
10
|
-
const
|
|
66
|
+
const usePersistedOperations = (rawOptions) => {
|
|
67
|
+
const options = {
|
|
68
|
+
...DEFAULT_OPTIONS,
|
|
69
|
+
...(rawOptions || {}),
|
|
70
|
+
};
|
|
11
71
|
return {
|
|
12
|
-
onParse({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const result = typeof rawResult === 'string' ? parse(rawResult) : rawResult;
|
|
18
|
-
if (writeToContext) {
|
|
19
|
-
extendContext({
|
|
20
|
-
[symbolInContext]: params.source,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
setParsedDocument(result);
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
throw new NonPersistedOperationError(`The operation hash "${params.source}" is not valid`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
if (options.onlyPersistedOperations) {
|
|
31
|
-
throw new GraphQLError(`Failed to handle GraphQL persisted operation.`);
|
|
32
|
-
}
|
|
72
|
+
onParse({ context, params, extendContext, setParsedDocument }) {
|
|
73
|
+
const operationId = options.extractOperationId ? options.extractOperationId(context) : operationIdFromSource(params.source);
|
|
74
|
+
if (!operationId) {
|
|
75
|
+
if (options.onlyPersisted) {
|
|
76
|
+
throw new GraphQLError('Must provide operation id');
|
|
33
77
|
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const store = typeof options.store === 'function' ? options.store(context) : options.store;
|
|
81
|
+
if (!store) {
|
|
82
|
+
throw new GraphQLError('Must provide store for persisted-operations!');
|
|
83
|
+
}
|
|
84
|
+
const rawResult = store.get(operationId);
|
|
85
|
+
if (rawResult) {
|
|
86
|
+
const document = typeof rawResult === 'string' ? parse(rawResult) : rawResult;
|
|
87
|
+
extendContext({ [contextProperty]: operationId });
|
|
88
|
+
setParsedDocument(document);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (options.onlyPersisted) {
|
|
92
|
+
// we want to throw an error only when "onlyPersisted" is true, otherwise we let execution continue normally
|
|
93
|
+
throw new GraphQLError(`Unable to match operation with id '${operationId}'`);
|
|
34
94
|
}
|
|
95
|
+
// if we reach this stage we could not retrieve a persisted operation and we didn't throw any error as onlyPersisted is false
|
|
96
|
+
// hence we let operation continue assuming consumer is not passing an operation id, but a plain query string, with current request.
|
|
35
97
|
},
|
|
36
98
|
};
|
|
37
99
|
};
|
|
38
100
|
|
|
39
|
-
export {
|
|
40
|
-
//# sourceMappingURL=index.mjs.map
|
|
101
|
+
export { AggregatedStore, InMemoryStore, JsonFileStore, operationIdFromSource, readOperationId, usePersistedOperations };
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/persisted-operations",
|
|
3
|
-
"version": "1.0.0",
|
|
3
|
+
"version": "2.1.0-alpha-e8998c9.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"graphql": "^14.0.0 || ^15.0.0"
|
|
6
|
+
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
7
7
|
},
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
package/plugin.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Plugin, DefaultContext } from '@envelop/types';
|
|
2
|
+
import { PersistedOperationsFunctionStore, PersistedOperationsStore } from './types';
|
|
3
|
+
export declare type UsePersistedOperationsOptions<ContextType = DefaultContext> = {
|
|
4
|
+
/**
|
|
5
|
+
* Set to `false` to allow running operations that are not available in the store.
|
|
6
|
+
* Set to `true` to allow running only persisted operations.
|
|
7
|
+
*/
|
|
8
|
+
onlyPersisted?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The store to use. You can implement a store that loads data from any source.
|
|
11
|
+
* You can even support multiple stores and implement a function that returns one of those stores based on context values.
|
|
12
|
+
*/
|
|
13
|
+
store: PersistedOperationsStore | PersistedOperationsFunctionStore<ContextType>;
|
|
14
|
+
/**
|
|
15
|
+
* Function that returns the operation id, e.g. by retrieving it from cusotm properties within context
|
|
16
|
+
*/
|
|
17
|
+
extractOperationId?: (context: Readonly<ContextType>) => string | undefined;
|
|
18
|
+
};
|
|
19
|
+
declare const contextProperty: unique symbol;
|
|
20
|
+
export declare type PersistedOperationPluginContext = {
|
|
21
|
+
[contextProperty]: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function readOperationId(context: PersistedOperationPluginContext): string;
|
|
24
|
+
export declare const usePersistedOperations: (rawOptions: UsePersistedOperationsOptions) => Plugin<PersistedOperationPluginContext>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DocumentNode } from 'graphql';
|
|
2
|
+
import { PersistedOperationsStore } from '../types';
|
|
3
|
+
export declare class AggregatedStore implements PersistedOperationsStore {
|
|
4
|
+
private stores;
|
|
5
|
+
constructor(stores: PersistedOperationsStore[]);
|
|
6
|
+
get(operationId: string): string | DocumentNode | undefined;
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DocumentNode } from 'graphql';
|
|
2
|
+
import { PersistedOperationsStore } from '../types';
|
|
3
|
+
export declare type InMemoryStoreDataMap = Map<string, DocumentNode | string>;
|
|
4
|
+
export declare class InMemoryStore implements PersistedOperationsStore {
|
|
5
|
+
private storeData;
|
|
6
|
+
constructor(options?: {
|
|
7
|
+
initialData?: InMemoryStoreDataMap;
|
|
8
|
+
});
|
|
9
|
+
get(operationId: string): string | DocumentNode | undefined;
|
|
10
|
+
prime(operationId: string, document: string | DocumentNode): void;
|
|
11
|
+
clear(operationId: string): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DocumentNode } from 'graphql';
|
|
2
|
+
import { PersistedOperationsStore } from '../types';
|
|
3
|
+
export declare type JsonFileStoreDataMap = Map<string, DocumentNode | string>;
|
|
4
|
+
export declare class JsonFileStore implements PersistedOperationsStore {
|
|
5
|
+
private storeData;
|
|
6
|
+
get(operationId: string): string | DocumentNode | undefined;
|
|
7
|
+
loadFromFileSync(path: string): void;
|
|
8
|
+
loadFromFile(path: string): Promise<void>;
|
|
9
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DefaultContext } from '@envelop/core';
|
|
2
|
+
import { DocumentNode } from 'graphql';
|
|
3
|
+
export interface PersistedOperationsStore {
|
|
4
|
+
/**
|
|
5
|
+
* Transfroms a hash/id into a DocumentNode or SDL string.
|
|
6
|
+
* Prefer returning `DocumentNode` if available to avoid additional parsing phase.
|
|
7
|
+
* Return `null` in case of a store miss.
|
|
8
|
+
* @param operationId
|
|
9
|
+
*/
|
|
10
|
+
get(operationId: string): string | DocumentNode | undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare type PersistedOperationsFunctionStore<ContextType = DefaultContext> = (context: Readonly<ContextType>) => PersistedOperationsStore;
|
package/utils.d.ts
ADDED
package/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../dist/plugins/persisted-operations/src/index.js"],"sourcesContent":["import { GraphQLError, parse } from 'graphql';\nexport class NonPersistedOperationError extends GraphQLError {\n}\nconst symbolInContext = Symbol('operationId');\nexport function readOperationId(context) {\n return context[symbolInContext];\n}\nexport const usePersistedOperations = (options) => {\n const writeToContext = options.writeToContext !== false;\n return {\n onParse({ params, setParsedDocument, extendContext }) {\n if (typeof params.source === 'string') {\n if (options.store.canHandle(params.source)) {\n const rawResult = options.store.get(params.source);\n if (rawResult) {\n const result = typeof rawResult === 'string' ? parse(rawResult) : rawResult;\n if (writeToContext) {\n extendContext({\n [symbolInContext]: params.source,\n });\n }\n setParsedDocument(result);\n }\n else {\n throw new NonPersistedOperationError(`The operation hash \"${params.source}\" is not valid`);\n }\n }\n else {\n if (options.onlyPersistedOperations) {\n throw new GraphQLError(`Failed to handle GraphQL persisted operation.`);\n }\n }\n }\n },\n };\n};\n//# sourceMappingURL=index.js.map"],"names":["GraphQLError","parse"],"mappings":";;;;;;AACO,MAAM,0BAA0B,SAASA,oBAAY,CAAC;AAC7D,CAAC;AACD,MAAM,eAAe,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;AACvC,SAAS,eAAe,CAAC,OAAO,EAAE;AACzC,IAAI,OAAO,OAAO,CAAC,eAAe,CAAC,CAAC;AACpC,CAAC;AACW,MAAC,sBAAsB,GAAG,CAAC,OAAO,KAAK;AACnD,IAAI,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC;AAC5D,IAAI,OAAO;AACX,QAAQ,OAAO,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,EAAE;AAC9D,YAAY,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE;AACnD,gBAAgB,IAAI,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;AAC5D,oBAAoB,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACvE,oBAAoB,IAAI,SAAS,EAAE;AACnC,wBAAwB,MAAM,MAAM,GAAG,OAAO,SAAS,KAAK,QAAQ,GAAGC,aAAK,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;AACpG,wBAAwB,IAAI,cAAc,EAAE;AAC5C,4BAA4B,aAAa,CAAC;AAC1C,gCAAgC,CAAC,eAAe,GAAG,MAAM,CAAC,MAAM;AAChE,6BAA6B,CAAC,CAAC;AAC/B,yBAAyB;AACzB,wBAAwB,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAClD,qBAAqB;AACrB,yBAAyB;AACzB,wBAAwB,MAAM,IAAI,0BAA0B,CAAC,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AACnH,qBAAqB;AACrB,iBAAiB;AACjB,qBAAqB;AACrB,oBAAoB,IAAI,OAAO,CAAC,uBAAuB,EAAE;AACzD,wBAAwB,MAAM,IAAID,oBAAY,CAAC,CAAC,6CAA6C,CAAC,CAAC,CAAC;AAChG,qBAAqB;AACrB,iBAAiB;AACjB,aAAa;AACb,SAAS;AACT,KAAK,CAAC;AACN;;;;;;"}
|
package/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../../dist/plugins/persisted-operations/src/index.js"],"sourcesContent":["import { GraphQLError, parse } from 'graphql';\nexport class NonPersistedOperationError extends GraphQLError {\n}\nconst symbolInContext = Symbol('operationId');\nexport function readOperationId(context) {\n return context[symbolInContext];\n}\nexport const usePersistedOperations = (options) => {\n const writeToContext = options.writeToContext !== false;\n return {\n onParse({ params, setParsedDocument, extendContext }) {\n if (typeof params.source === 'string') {\n if (options.store.canHandle(params.source)) {\n const rawResult = options.store.get(params.source);\n if (rawResult) {\n const result = typeof rawResult === 'string' ? parse(rawResult) : rawResult;\n if (writeToContext) {\n extendContext({\n [symbolInContext]: params.source,\n });\n }\n setParsedDocument(result);\n }\n else {\n throw new NonPersistedOperationError(`The operation hash \"${params.source}\" is not valid`);\n }\n }\n else {\n if (options.onlyPersistedOperations) {\n throw new GraphQLError(`Failed to handle GraphQL persisted operation.`);\n }\n }\n }\n },\n };\n};\n//# sourceMappingURL=index.js.map"],"names":[],"mappings":";;AACO,MAAM,0BAA0B,SAAS,YAAY,CAAC;AAC7D,CAAC;AACD,MAAM,eAAe,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;AACvC,SAAS,eAAe,CAAC,OAAO,EAAE;AACzC,IAAI,OAAO,OAAO,CAAC,eAAe,CAAC,CAAC;AACpC,CAAC;AACW,MAAC,sBAAsB,GAAG,CAAC,OAAO,KAAK;AACnD,IAAI,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC;AAC5D,IAAI,OAAO;AACX,QAAQ,OAAO,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,EAAE;AAC9D,YAAY,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE;AACnD,gBAAgB,IAAI,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;AAC5D,oBAAoB,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACvE,oBAAoB,IAAI,SAAS,EAAE;AACnC,wBAAwB,MAAM,MAAM,GAAG,OAAO,SAAS,KAAK,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;AACpG,wBAAwB,IAAI,cAAc,EAAE;AAC5C,4BAA4B,aAAa,CAAC;AAC1C,gCAAgC,CAAC,eAAe,GAAG,MAAM,CAAC,MAAM;AAChE,6BAA6B,CAAC,CAAC;AAC/B,yBAAyB;AACzB,wBAAwB,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAClD,qBAAqB;AACrB,yBAAyB;AACzB,wBAAwB,MAAM,IAAI,0BAA0B,CAAC,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AACnH,qBAAqB;AACrB,iBAAiB;AACjB,qBAAqB;AACrB,oBAAoB,IAAI,OAAO,CAAC,uBAAuB,EAAE;AACzD,wBAAwB,MAAM,IAAI,YAAY,CAAC,CAAC,6CAA6C,CAAC,CAAC,CAAC;AAChG,qBAAqB;AACrB,iBAAiB;AACjB,aAAa;AACb,SAAS;AACT,KAAK,CAAC;AACN;;;;"}
|