@envelop/response-cache 3.0.0-alpha-6a4e0e2.0 → 3.0.0-alpha-e8a9318.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/cache-document-str.js +26 -0
- package/cjs/cache.js +2 -0
- package/cjs/hashSHA256.js +66 -0
- package/cjs/in-memory-cache.js +86 -0
- package/cjs/index.js +6 -0
- package/cjs/package.json +1 -0
- package/{index.js → cjs/plugin.js} +31 -189
- package/esm/cache-document-str.js +21 -0
- package/esm/cache.js +1 -0
- package/esm/hashSHA256.js +62 -0
- package/esm/in-memory-cache.js +80 -0
- package/esm/index.js +3 -0
- package/{index.mjs → esm/plugin.js} +15 -169
- package/package.json +35 -14
- package/typings/cache-document-str.d.ts +4 -0
- package/{cache.d.ts → typings/cache.d.ts} +0 -0
- package/{hashSHA256.d.ts → typings/hashSHA256.d.ts} +0 -0
- package/{in-memory-cache.d.ts → typings/in-memory-cache.d.ts} +2 -2
- package/typings/index.d.ts +3 -0
- package/{plugin.d.ts → typings/plugin.d.ts} +6 -3
- package/index.d.ts +0 -3
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultGetDocumentString = exports.useCacheDocumentString = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
const documentStringByDocument = new WeakMap();
|
|
6
|
+
function useCacheDocumentString() {
|
|
7
|
+
return {
|
|
8
|
+
onParse({ params: { source } }) {
|
|
9
|
+
return function onParseEnd({ result }) {
|
|
10
|
+
if (result != null && !(result instanceof Error)) {
|
|
11
|
+
documentStringByDocument.set(result, source.toString());
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
exports.useCacheDocumentString = useCacheDocumentString;
|
|
18
|
+
function defaultGetDocumentString(executionArgs) {
|
|
19
|
+
let documentString = documentStringByDocument.get(executionArgs.document);
|
|
20
|
+
if (documentString == null) {
|
|
21
|
+
documentString = (0, graphql_1.print)(executionArgs.document);
|
|
22
|
+
documentStringByDocument.set(executionArgs.document, documentString);
|
|
23
|
+
}
|
|
24
|
+
return documentString;
|
|
25
|
+
}
|
|
26
|
+
exports.defaultGetDocumentString = defaultGetDocumentString;
|
package/cjs/cache.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashSHA256 = void 0;
|
|
4
|
+
const webCrypto = globalThis.crypto;
|
|
5
|
+
const sha256Browser = (bytes) => {
|
|
6
|
+
const hash = webCrypto.subtle.digest({ name: 'SHA-256' }, bytes);
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
Promise.resolve(hash)
|
|
9
|
+
.then(function (result) {
|
|
10
|
+
resolve(new Uint8Array(result));
|
|
11
|
+
})
|
|
12
|
+
.catch(function (error) {
|
|
13
|
+
reject(error);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
let nodeCrypto;
|
|
18
|
+
if (typeof window === 'undefined' && !webCrypto) {
|
|
19
|
+
// Indirect eval'd require/import to guarantee no side-effects in module scope
|
|
20
|
+
// (optimization for minifier)
|
|
21
|
+
try {
|
|
22
|
+
// eslint-disable-next-line no-new-func
|
|
23
|
+
nodeCrypto = Promise.resolve(new Function('require', 'return require("crypto")')(require));
|
|
24
|
+
}
|
|
25
|
+
catch (_error) {
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line no-new-func
|
|
28
|
+
nodeCrypto = new Function('return import("crypto")')();
|
|
29
|
+
}
|
|
30
|
+
catch (_error) { }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const hashSHA256 = async (text) => {
|
|
34
|
+
// Node.js support
|
|
35
|
+
if (nodeCrypto) {
|
|
36
|
+
return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
|
|
37
|
+
}
|
|
38
|
+
if (webCrypto) {
|
|
39
|
+
let buf;
|
|
40
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
41
|
+
buf = new TextEncoder().encode(text);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
buf = new Uint8Array(text.length);
|
|
45
|
+
for (let i = 0, l = text.length; i < l; i++) {
|
|
46
|
+
// NOTE: We assume that the input GraphQL Query only uses UTF-8 at most
|
|
47
|
+
// since GraphQL mostly consists of ASCII, this is completely fine
|
|
48
|
+
buf[i] = text.charCodeAt(i);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const out = await sha256Browser(buf);
|
|
52
|
+
let hash = '';
|
|
53
|
+
for (let i = 0, l = out.length; i < l; i++) {
|
|
54
|
+
const hex = out[i].toString(16);
|
|
55
|
+
hash += '00'.slice(0, Math.max(0, 2 - hex.length)) + hex;
|
|
56
|
+
}
|
|
57
|
+
return hash;
|
|
58
|
+
}
|
|
59
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
60
|
+
// eslint-disable-next-line no-console
|
|
61
|
+
console.warn('[@envelop/response-cache]: The Node Crypto and Web Crypto APIs are not available.\n' +
|
|
62
|
+
'This is an unexpected error. Please report it by filing a GitHub Issue.');
|
|
63
|
+
}
|
|
64
|
+
return Promise.resolve('');
|
|
65
|
+
};
|
|
66
|
+
exports.hashSHA256 = hashSHA256;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultBuildEntityId = exports.createInMemoryCache = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lru_cache_1 = tslib_1.__importDefault(require("lru-cache"));
|
|
6
|
+
const createInMemoryCache = (params) => {
|
|
7
|
+
var _a;
|
|
8
|
+
const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : exports.defaultBuildEntityId;
|
|
9
|
+
const cachedResponses = new lru_cache_1.default({
|
|
10
|
+
max: params === null || params === void 0 ? void 0 : params.max,
|
|
11
|
+
stale: false,
|
|
12
|
+
noDisposeOnSet: true,
|
|
13
|
+
dispose(responseId) {
|
|
14
|
+
purgeResponse(responseId, false);
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const entityToResponseIds = new Map();
|
|
18
|
+
const responseIdToEntityIds = new Map();
|
|
19
|
+
function purgeResponse(responseId, shouldRemove = true) {
|
|
20
|
+
var _a;
|
|
21
|
+
const entityIds = responseIdToEntityIds.get(responseId);
|
|
22
|
+
// get entities related to the response
|
|
23
|
+
if (entityIds !== undefined) {
|
|
24
|
+
for (const entityId of entityIds) {
|
|
25
|
+
// remove the response mapping from the entity
|
|
26
|
+
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
27
|
+
}
|
|
28
|
+
// remove all the entity mappings from the response
|
|
29
|
+
responseIdToEntityIds.delete(responseId);
|
|
30
|
+
}
|
|
31
|
+
if (shouldRemove) {
|
|
32
|
+
// remove the response from the cache
|
|
33
|
+
cachedResponses.del(responseId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function purgeEntity(entity) {
|
|
37
|
+
const responseIds = entityToResponseIds.get(entity);
|
|
38
|
+
if (responseIds !== undefined) {
|
|
39
|
+
for (const responseId of responseIds) {
|
|
40
|
+
purgeResponse(responseId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
set(responseId, result, collectedEntities, ttl) {
|
|
46
|
+
cachedResponses.set(responseId, result, ttl);
|
|
47
|
+
const entityIds = new Set();
|
|
48
|
+
responseIdToEntityIds.set(responseId, entityIds);
|
|
49
|
+
for (const { typename, id } of collectedEntities) {
|
|
50
|
+
let operationIds = entityToResponseIds.get(typename);
|
|
51
|
+
if (operationIds == null) {
|
|
52
|
+
operationIds = new Set();
|
|
53
|
+
entityToResponseIds.set(typename, operationIds);
|
|
54
|
+
}
|
|
55
|
+
// typename => operation
|
|
56
|
+
operationIds.add(responseId);
|
|
57
|
+
// operation => typename
|
|
58
|
+
entityIds.add(typename);
|
|
59
|
+
if (id !== undefined) {
|
|
60
|
+
const entityId = buildEntityId(typename, id);
|
|
61
|
+
let responseIds = entityToResponseIds.get(entityId);
|
|
62
|
+
if (responseIds == null) {
|
|
63
|
+
responseIds = new Set();
|
|
64
|
+
entityToResponseIds.set(entityId, responseIds);
|
|
65
|
+
}
|
|
66
|
+
// typename:id => operation
|
|
67
|
+
responseIds.add(responseId);
|
|
68
|
+
// operation => typename:id
|
|
69
|
+
entityIds.add(entityId);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
get(responseId) {
|
|
74
|
+
var _a;
|
|
75
|
+
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
76
|
+
},
|
|
77
|
+
invalidate(entitiesToRemove) {
|
|
78
|
+
for (const { typename, id } of entitiesToRemove) {
|
|
79
|
+
purgeEntity(id != null ? buildEntityId(typename, id) : typename);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
exports.createInMemoryCache = createInMemoryCache;
|
|
85
|
+
const defaultBuildEntityId = (typename, id) => `${typename}:${id}`;
|
|
86
|
+
exports.defaultBuildEntityId = defaultBuildEntityId;
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./in-memory-cache.js"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./plugin.js"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./cache.js"), exports);
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -1,158 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const createInMemoryCache = (params) => {
|
|
14
|
-
var _a;
|
|
15
|
-
const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : defaultBuildEntityId;
|
|
16
|
-
const cachedResponses = new LRU({
|
|
17
|
-
max: params === null || params === void 0 ? void 0 : params.max,
|
|
18
|
-
stale: false,
|
|
19
|
-
noDisposeOnSet: true,
|
|
20
|
-
dispose(responseId) {
|
|
21
|
-
purgeResponse(responseId, false);
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
const entityToResponseIds = new Map();
|
|
25
|
-
const responseIdToEntityIds = new Map();
|
|
26
|
-
function purgeResponse(responseId, shouldRemove = true) {
|
|
27
|
-
var _a;
|
|
28
|
-
const entityIds = responseIdToEntityIds.get(responseId);
|
|
29
|
-
// get entities related to the response
|
|
30
|
-
if (entityIds !== undefined) {
|
|
31
|
-
for (const entityId of entityIds) {
|
|
32
|
-
// remove the response mapping from the entity
|
|
33
|
-
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
34
|
-
}
|
|
35
|
-
// remove all the entity mappings from the response
|
|
36
|
-
responseIdToEntityIds.delete(responseId);
|
|
37
|
-
}
|
|
38
|
-
if (shouldRemove) {
|
|
39
|
-
// remove the response from the cache
|
|
40
|
-
cachedResponses.del(responseId);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function purgeEntity(entity) {
|
|
44
|
-
const responseIds = entityToResponseIds.get(entity);
|
|
45
|
-
if (responseIds !== undefined) {
|
|
46
|
-
for (const responseId of responseIds) {
|
|
47
|
-
purgeResponse(responseId);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
set(responseId, result, collectedEntities, ttl) {
|
|
53
|
-
cachedResponses.set(responseId, result, ttl);
|
|
54
|
-
const entityIds = new Set();
|
|
55
|
-
responseIdToEntityIds.set(responseId, entityIds);
|
|
56
|
-
for (const { typename, id } of collectedEntities) {
|
|
57
|
-
let operationIds = entityToResponseIds.get(typename);
|
|
58
|
-
if (operationIds == null) {
|
|
59
|
-
operationIds = new Set();
|
|
60
|
-
entityToResponseIds.set(typename, operationIds);
|
|
61
|
-
}
|
|
62
|
-
// typename => operation
|
|
63
|
-
operationIds.add(responseId);
|
|
64
|
-
// operation => typename
|
|
65
|
-
entityIds.add(typename);
|
|
66
|
-
if (id !== undefined) {
|
|
67
|
-
const entityId = buildEntityId(typename, id);
|
|
68
|
-
let responseIds = entityToResponseIds.get(entityId);
|
|
69
|
-
if (responseIds == null) {
|
|
70
|
-
responseIds = new Set();
|
|
71
|
-
entityToResponseIds.set(entityId, responseIds);
|
|
72
|
-
}
|
|
73
|
-
// typename:id => operation
|
|
74
|
-
responseIds.add(responseId);
|
|
75
|
-
// operation => typename:id
|
|
76
|
-
entityIds.add(entityId);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
get(responseId) {
|
|
81
|
-
var _a;
|
|
82
|
-
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
83
|
-
},
|
|
84
|
-
invalidate(entitiesToRemove) {
|
|
85
|
-
for (const { typename, id } of entitiesToRemove) {
|
|
86
|
-
purgeEntity(id != null ? buildEntityId(typename, id) : typename);
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
};
|
|
91
|
-
const defaultBuildEntityId = (typename, id) => `${typename}:${id}`;
|
|
92
|
-
|
|
93
|
-
const webCrypto = globalThis.crypto;
|
|
94
|
-
const sha256Browser = (bytes) => {
|
|
95
|
-
const hash = webCrypto.subtle.digest({ name: 'SHA-256' }, bytes);
|
|
96
|
-
return new Promise((resolve, reject) => {
|
|
97
|
-
Promise.resolve(hash)
|
|
98
|
-
.then(function (result) {
|
|
99
|
-
resolve(new Uint8Array(result));
|
|
100
|
-
})
|
|
101
|
-
.catch(function (error) {
|
|
102
|
-
reject(error);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
};
|
|
106
|
-
let nodeCrypto;
|
|
107
|
-
if (typeof window === 'undefined' && !webCrypto) {
|
|
108
|
-
// Indirect eval'd require/import to guarantee no side-effects in module scope
|
|
109
|
-
// (optimization for minifier)
|
|
110
|
-
try {
|
|
111
|
-
// eslint-disable-next-line no-new-func
|
|
112
|
-
nodeCrypto = Promise.resolve(new Function('require', 'return require("crypto")')(require));
|
|
113
|
-
}
|
|
114
|
-
catch (_error) {
|
|
115
|
-
try {
|
|
116
|
-
// eslint-disable-next-line no-new-func
|
|
117
|
-
nodeCrypto = new Function('return import("crypto")')();
|
|
118
|
-
}
|
|
119
|
-
catch (_error) { }
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const hashSHA256 = async (text) => {
|
|
123
|
-
// Node.js support
|
|
124
|
-
if (nodeCrypto) {
|
|
125
|
-
return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
|
|
126
|
-
}
|
|
127
|
-
if (webCrypto) {
|
|
128
|
-
let buf;
|
|
129
|
-
if (typeof TextEncoder !== 'undefined') {
|
|
130
|
-
buf = new TextEncoder().encode(text);
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
buf = new Uint8Array(text.length);
|
|
134
|
-
for (let i = 0, l = text.length; i < l; i++) {
|
|
135
|
-
// NOTE: We assume that the input GraphQL Query only uses UTF-8 at most
|
|
136
|
-
// since GraphQL mostly consists of ASCII, this is completely fine
|
|
137
|
-
buf[i] = text.charCodeAt(i);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const out = await sha256Browser(buf);
|
|
141
|
-
let hash = '';
|
|
142
|
-
for (let i = 0, l = out.length; i < l; i++) {
|
|
143
|
-
const hex = out[i].toString(16);
|
|
144
|
-
hash += '00'.slice(0, Math.max(0, 2 - hex.length)) + hex;
|
|
145
|
-
}
|
|
146
|
-
return hash;
|
|
147
|
-
}
|
|
148
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
149
|
-
// eslint-disable-next-line no-console
|
|
150
|
-
console.warn('[@envelop/response-cache]: The Node Crypto and Web Crypto APIs are not available.\n' +
|
|
151
|
-
'This is an unexpected error. Please report it by filing a GitHub Issue.');
|
|
152
|
-
}
|
|
153
|
-
return Promise.resolve('');
|
|
154
|
-
};
|
|
155
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useResponseCache = exports.defaultShouldCacheResult = exports.defaultBuildResponseCacheKey = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const core_1 = require("@envelop/core");
|
|
6
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
7
|
+
const graphql_1 = require("graphql");
|
|
8
|
+
const fast_json_stable_stringify_1 = tslib_1.__importDefault(require("fast-json-stable-stringify"));
|
|
9
|
+
const in_memory_cache_js_1 = require("./in-memory-cache.js");
|
|
10
|
+
const hashSHA256_js_1 = require("./hashSHA256.js");
|
|
11
|
+
const cache_document_str_js_1 = require("./cache-document-str.js");
|
|
156
12
|
const contextSymbol = Symbol('responseCache');
|
|
157
13
|
/**
|
|
158
14
|
* Default function used for building the response cache key.
|
|
@@ -160,13 +16,14 @@ const contextSymbol = Symbol('responseCache');
|
|
|
160
16
|
*/
|
|
161
17
|
const defaultBuildResponseCacheKey = params => {
|
|
162
18
|
var _a, _b, _c;
|
|
163
|
-
return hashSHA256([
|
|
19
|
+
return (0, hashSHA256_js_1.hashSHA256)([
|
|
164
20
|
params.documentString,
|
|
165
21
|
(_a = params.operationName) !== null && _a !== void 0 ? _a : '',
|
|
166
|
-
|
|
22
|
+
(0, fast_json_stable_stringify_1.default)((_b = params.variableValues) !== null && _b !== void 0 ? _b : {}),
|
|
167
23
|
(_c = params.sessionId) !== null && _c !== void 0 ? _c : '',
|
|
168
24
|
].join('|'));
|
|
169
25
|
};
|
|
26
|
+
exports.defaultBuildResponseCacheKey = defaultBuildResponseCacheKey;
|
|
170
27
|
/**
|
|
171
28
|
* Default function used to check if the result should be cached.
|
|
172
29
|
*
|
|
@@ -183,16 +40,8 @@ const defaultShouldCacheResult = (params) => {
|
|
|
183
40
|
}
|
|
184
41
|
return true;
|
|
185
42
|
};
|
|
186
|
-
|
|
187
|
-
function
|
|
188
|
-
let documentString = documentStringByDocument.get(executionArgs.document);
|
|
189
|
-
if (!documentString) {
|
|
190
|
-
documentString = graphql.print(executionArgs.document);
|
|
191
|
-
documentStringByDocument.set(executionArgs.document, documentString);
|
|
192
|
-
}
|
|
193
|
-
return documentString;
|
|
194
|
-
}
|
|
195
|
-
function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl = Infinity, session = () => null, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = defaultBuildResponseCacheKey, getDocumentString = defaultGetDocumentString, shouldCacheResult = defaultShouldCacheResult,
|
|
43
|
+
exports.defaultShouldCacheResult = defaultShouldCacheResult;
|
|
44
|
+
function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache)(), ttl: globalTtl = Infinity, session = () => null, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = exports.defaultBuildResponseCacheKey, getDocumentString = cache_document_str_js_1.defaultGetDocumentString, shouldCacheResult = exports.defaultShouldCacheResult,
|
|
196
45
|
// eslint-disable-next-line dot-notation
|
|
197
46
|
includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false, } = {}) {
|
|
198
47
|
const appliedTransform = Symbol('responseCache.appliedTransform');
|
|
@@ -200,6 +49,11 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
200
49
|
// never cache Introspections
|
|
201
50
|
ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
|
|
202
51
|
return {
|
|
52
|
+
onPluginInit({ addPlugin }) {
|
|
53
|
+
if (getDocumentString === cache_document_str_js_1.defaultGetDocumentString) {
|
|
54
|
+
addPlugin((0, cache_document_str_js_1.useCacheDocumentString)());
|
|
55
|
+
}
|
|
56
|
+
},
|
|
203
57
|
onSchemaChange({ schema, replaceSchema }) {
|
|
204
58
|
var _a;
|
|
205
59
|
// @ts-expect-error See https://github.com/graphql/graphql-js/pull/3511 - remove this comments once merged
|
|
@@ -213,13 +67,6 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
213
67
|
};
|
|
214
68
|
replaceSchema(patchedSchema);
|
|
215
69
|
},
|
|
216
|
-
onParse({ params: { source } }) {
|
|
217
|
-
return function onParseEnd({ result }) {
|
|
218
|
-
if (result != null && !(result instanceof Error)) {
|
|
219
|
-
documentStringByDocument.set(result, source.toString());
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
},
|
|
223
70
|
async onExecute(ctx) {
|
|
224
71
|
var _a;
|
|
225
72
|
const identifier = new Map();
|
|
@@ -241,7 +88,7 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
241
88
|
}
|
|
242
89
|
return {
|
|
243
90
|
onExecuteDone({ result, setResult }) {
|
|
244
|
-
if (
|
|
91
|
+
if ((0, core_1.isAsyncIterable)(result)) {
|
|
245
92
|
// eslint-disable-next-line no-console
|
|
246
93
|
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
247
94
|
return;
|
|
@@ -263,8 +110,9 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
263
110
|
}
|
|
264
111
|
const operationId = await buildResponseCacheKey({
|
|
265
112
|
documentString: getDocumentString(ctx.args),
|
|
113
|
+
variableValues: ctx.args.variableValues,
|
|
114
|
+
operationName: ctx.args.operationName,
|
|
266
115
|
sessionId: session(ctx.args.contextValue),
|
|
267
|
-
...ctx.args,
|
|
268
116
|
});
|
|
269
117
|
if (((_a = enabled === null || enabled === void 0 ? void 0 : enabled(ctx.args.contextValue)) !== null && _a !== void 0 ? _a : true) === true) {
|
|
270
118
|
const cachedResponse = await cache.get(operationId);
|
|
@@ -286,8 +134,8 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
286
134
|
}
|
|
287
135
|
}
|
|
288
136
|
if (ttlPerSchemaCoordinate) {
|
|
289
|
-
const typeInfo = new
|
|
290
|
-
|
|
137
|
+
const typeInfo = new graphql_1.TypeInfo(ctx.args.schema);
|
|
138
|
+
(0, graphql_1.visit)(ctx.args.document, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
|
|
291
139
|
Field(fieldNode) {
|
|
292
140
|
const parentType = typeInfo.getParentType();
|
|
293
141
|
if (parentType) {
|
|
@@ -303,7 +151,7 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
303
151
|
return {
|
|
304
152
|
onExecuteDone({ result, setResult }) {
|
|
305
153
|
var _a;
|
|
306
|
-
if (
|
|
154
|
+
if ((0, core_1.isAsyncIterable)(result)) {
|
|
307
155
|
// eslint-disable-next-line no-console
|
|
308
156
|
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
309
157
|
return;
|
|
@@ -348,6 +196,7 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
348
196
|
},
|
|
349
197
|
};
|
|
350
198
|
}
|
|
199
|
+
exports.useResponseCache = useResponseCache;
|
|
351
200
|
function isOperationDefinition(node) {
|
|
352
201
|
return (node === null || node === void 0 ? void 0 : node.kind) === 'OperationDefinition';
|
|
353
202
|
}
|
|
@@ -369,14 +218,14 @@ function runWith(input, callback) {
|
|
|
369
218
|
}
|
|
370
219
|
}
|
|
371
220
|
function applyResponseCacheLogic(schema, idFieldNames) {
|
|
372
|
-
return
|
|
373
|
-
[
|
|
221
|
+
return (0, utils_1.mapSchema)(schema, {
|
|
222
|
+
[utils_1.MapperKind.OBJECT_FIELD](fieldConfig, fieldName, typename) {
|
|
374
223
|
if (idFieldNames.includes(fieldName)) {
|
|
375
224
|
return {
|
|
376
225
|
...fieldConfig,
|
|
377
226
|
resolve(src, args, context, info) {
|
|
378
227
|
var _a;
|
|
379
|
-
const result = ((_a = fieldConfig.resolve) !== null && _a !== void 0 ? _a :
|
|
228
|
+
const result = ((_a = fieldConfig.resolve) !== null && _a !== void 0 ? _a : graphql_1.defaultFieldResolver)(src, args, context, info);
|
|
380
229
|
runWith(result, (id) => {
|
|
381
230
|
const ctx = context[contextSymbol];
|
|
382
231
|
if (ctx !== undefined) {
|
|
@@ -404,10 +253,3 @@ function applyResponseCacheLogic(schema, idFieldNames) {
|
|
|
404
253
|
},
|
|
405
254
|
});
|
|
406
255
|
}
|
|
407
|
-
|
|
408
|
-
exports.createInMemoryCache = createInMemoryCache;
|
|
409
|
-
exports.defaultBuildEntityId = defaultBuildEntityId;
|
|
410
|
-
exports.defaultBuildResponseCacheKey = defaultBuildResponseCacheKey;
|
|
411
|
-
exports.defaultGetDocumentString = defaultGetDocumentString;
|
|
412
|
-
exports.defaultShouldCacheResult = defaultShouldCacheResult;
|
|
413
|
-
exports.useResponseCache = useResponseCache;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { print } from 'graphql';
|
|
2
|
+
const documentStringByDocument = new WeakMap();
|
|
3
|
+
export function useCacheDocumentString() {
|
|
4
|
+
return {
|
|
5
|
+
onParse({ params: { source } }) {
|
|
6
|
+
return function onParseEnd({ result }) {
|
|
7
|
+
if (result != null && !(result instanceof Error)) {
|
|
8
|
+
documentStringByDocument.set(result, source.toString());
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function defaultGetDocumentString(executionArgs) {
|
|
15
|
+
let documentString = documentStringByDocument.get(executionArgs.document);
|
|
16
|
+
if (documentString == null) {
|
|
17
|
+
documentString = print(executionArgs.document);
|
|
18
|
+
documentStringByDocument.set(executionArgs.document, documentString);
|
|
19
|
+
}
|
|
20
|
+
return documentString;
|
|
21
|
+
}
|
package/esm/cache.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const webCrypto = globalThis.crypto;
|
|
2
|
+
const sha256Browser = (bytes) => {
|
|
3
|
+
const hash = webCrypto.subtle.digest({ name: 'SHA-256' }, bytes);
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
Promise.resolve(hash)
|
|
6
|
+
.then(function (result) {
|
|
7
|
+
resolve(new Uint8Array(result));
|
|
8
|
+
})
|
|
9
|
+
.catch(function (error) {
|
|
10
|
+
reject(error);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
let nodeCrypto;
|
|
15
|
+
if (typeof window === 'undefined' && !webCrypto) {
|
|
16
|
+
// Indirect eval'd require/import to guarantee no side-effects in module scope
|
|
17
|
+
// (optimization for minifier)
|
|
18
|
+
try {
|
|
19
|
+
// eslint-disable-next-line no-new-func
|
|
20
|
+
nodeCrypto = Promise.resolve(new Function('require', 'return require("crypto")')(require));
|
|
21
|
+
}
|
|
22
|
+
catch (_error) {
|
|
23
|
+
try {
|
|
24
|
+
// eslint-disable-next-line no-new-func
|
|
25
|
+
nodeCrypto = new Function('return import("crypto")')();
|
|
26
|
+
}
|
|
27
|
+
catch (_error) { }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export const hashSHA256 = async (text) => {
|
|
31
|
+
// Node.js support
|
|
32
|
+
if (nodeCrypto) {
|
|
33
|
+
return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
|
|
34
|
+
}
|
|
35
|
+
if (webCrypto) {
|
|
36
|
+
let buf;
|
|
37
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
38
|
+
buf = new TextEncoder().encode(text);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
buf = new Uint8Array(text.length);
|
|
42
|
+
for (let i = 0, l = text.length; i < l; i++) {
|
|
43
|
+
// NOTE: We assume that the input GraphQL Query only uses UTF-8 at most
|
|
44
|
+
// since GraphQL mostly consists of ASCII, this is completely fine
|
|
45
|
+
buf[i] = text.charCodeAt(i);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const out = await sha256Browser(buf);
|
|
49
|
+
let hash = '';
|
|
50
|
+
for (let i = 0, l = out.length; i < l; i++) {
|
|
51
|
+
const hex = out[i].toString(16);
|
|
52
|
+
hash += '00'.slice(0, Math.max(0, 2 - hex.length)) + hex;
|
|
53
|
+
}
|
|
54
|
+
return hash;
|
|
55
|
+
}
|
|
56
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.warn('[@envelop/response-cache]: The Node Crypto and Web Crypto APIs are not available.\n' +
|
|
59
|
+
'This is an unexpected error. Please report it by filing a GitHub Issue.');
|
|
60
|
+
}
|
|
61
|
+
return Promise.resolve('');
|
|
62
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import LRU from 'lru-cache';
|
|
2
|
+
export const createInMemoryCache = (params) => {
|
|
3
|
+
var _a;
|
|
4
|
+
const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : defaultBuildEntityId;
|
|
5
|
+
const cachedResponses = new LRU({
|
|
6
|
+
max: params === null || params === void 0 ? void 0 : params.max,
|
|
7
|
+
stale: false,
|
|
8
|
+
noDisposeOnSet: true,
|
|
9
|
+
dispose(responseId) {
|
|
10
|
+
purgeResponse(responseId, false);
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
const entityToResponseIds = new Map();
|
|
14
|
+
const responseIdToEntityIds = new Map();
|
|
15
|
+
function purgeResponse(responseId, shouldRemove = true) {
|
|
16
|
+
var _a;
|
|
17
|
+
const entityIds = responseIdToEntityIds.get(responseId);
|
|
18
|
+
// get entities related to the response
|
|
19
|
+
if (entityIds !== undefined) {
|
|
20
|
+
for (const entityId of entityIds) {
|
|
21
|
+
// remove the response mapping from the entity
|
|
22
|
+
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
23
|
+
}
|
|
24
|
+
// remove all the entity mappings from the response
|
|
25
|
+
responseIdToEntityIds.delete(responseId);
|
|
26
|
+
}
|
|
27
|
+
if (shouldRemove) {
|
|
28
|
+
// remove the response from the cache
|
|
29
|
+
cachedResponses.del(responseId);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function purgeEntity(entity) {
|
|
33
|
+
const responseIds = entityToResponseIds.get(entity);
|
|
34
|
+
if (responseIds !== undefined) {
|
|
35
|
+
for (const responseId of responseIds) {
|
|
36
|
+
purgeResponse(responseId);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
set(responseId, result, collectedEntities, ttl) {
|
|
42
|
+
cachedResponses.set(responseId, result, ttl);
|
|
43
|
+
const entityIds = new Set();
|
|
44
|
+
responseIdToEntityIds.set(responseId, entityIds);
|
|
45
|
+
for (const { typename, id } of collectedEntities) {
|
|
46
|
+
let operationIds = entityToResponseIds.get(typename);
|
|
47
|
+
if (operationIds == null) {
|
|
48
|
+
operationIds = new Set();
|
|
49
|
+
entityToResponseIds.set(typename, operationIds);
|
|
50
|
+
}
|
|
51
|
+
// typename => operation
|
|
52
|
+
operationIds.add(responseId);
|
|
53
|
+
// operation => typename
|
|
54
|
+
entityIds.add(typename);
|
|
55
|
+
if (id !== undefined) {
|
|
56
|
+
const entityId = buildEntityId(typename, id);
|
|
57
|
+
let responseIds = entityToResponseIds.get(entityId);
|
|
58
|
+
if (responseIds == null) {
|
|
59
|
+
responseIds = new Set();
|
|
60
|
+
entityToResponseIds.set(entityId, responseIds);
|
|
61
|
+
}
|
|
62
|
+
// typename:id => operation
|
|
63
|
+
responseIds.add(responseId);
|
|
64
|
+
// operation => typename:id
|
|
65
|
+
entityIds.add(entityId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
get(responseId) {
|
|
70
|
+
var _a;
|
|
71
|
+
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
72
|
+
},
|
|
73
|
+
invalidate(entitiesToRemove) {
|
|
74
|
+
for (const { typename, id } of entitiesToRemove) {
|
|
75
|
+
purgeEntity(id != null ? buildEntityId(typename, id) : typename);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
export const defaultBuildEntityId = (typename, id) => `${typename}:${id}`;
|
package/esm/index.js
ADDED
|
@@ -1,158 +1,16 @@
|
|
|
1
|
-
import LRU from 'lru-cache';
|
|
2
1
|
import { isAsyncIterable } from '@envelop/core';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { MapperKind, mapSchema } from '@graphql-tools/utils';
|
|
3
|
+
import { visit, TypeInfo, visitWithTypeInfo, defaultFieldResolver, } from 'graphql';
|
|
5
4
|
import jsonStableStringify from 'fast-json-stable-stringify';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : defaultBuildEntityId;
|
|
10
|
-
const cachedResponses = new LRU({
|
|
11
|
-
max: params === null || params === void 0 ? void 0 : params.max,
|
|
12
|
-
stale: false,
|
|
13
|
-
noDisposeOnSet: true,
|
|
14
|
-
dispose(responseId) {
|
|
15
|
-
purgeResponse(responseId, false);
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
const entityToResponseIds = new Map();
|
|
19
|
-
const responseIdToEntityIds = new Map();
|
|
20
|
-
function purgeResponse(responseId, shouldRemove = true) {
|
|
21
|
-
var _a;
|
|
22
|
-
const entityIds = responseIdToEntityIds.get(responseId);
|
|
23
|
-
// get entities related to the response
|
|
24
|
-
if (entityIds !== undefined) {
|
|
25
|
-
for (const entityId of entityIds) {
|
|
26
|
-
// remove the response mapping from the entity
|
|
27
|
-
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
28
|
-
}
|
|
29
|
-
// remove all the entity mappings from the response
|
|
30
|
-
responseIdToEntityIds.delete(responseId);
|
|
31
|
-
}
|
|
32
|
-
if (shouldRemove) {
|
|
33
|
-
// remove the response from the cache
|
|
34
|
-
cachedResponses.del(responseId);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function purgeEntity(entity) {
|
|
38
|
-
const responseIds = entityToResponseIds.get(entity);
|
|
39
|
-
if (responseIds !== undefined) {
|
|
40
|
-
for (const responseId of responseIds) {
|
|
41
|
-
purgeResponse(responseId);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
set(responseId, result, collectedEntities, ttl) {
|
|
47
|
-
cachedResponses.set(responseId, result, ttl);
|
|
48
|
-
const entityIds = new Set();
|
|
49
|
-
responseIdToEntityIds.set(responseId, entityIds);
|
|
50
|
-
for (const { typename, id } of collectedEntities) {
|
|
51
|
-
let operationIds = entityToResponseIds.get(typename);
|
|
52
|
-
if (operationIds == null) {
|
|
53
|
-
operationIds = new Set();
|
|
54
|
-
entityToResponseIds.set(typename, operationIds);
|
|
55
|
-
}
|
|
56
|
-
// typename => operation
|
|
57
|
-
operationIds.add(responseId);
|
|
58
|
-
// operation => typename
|
|
59
|
-
entityIds.add(typename);
|
|
60
|
-
if (id !== undefined) {
|
|
61
|
-
const entityId = buildEntityId(typename, id);
|
|
62
|
-
let responseIds = entityToResponseIds.get(entityId);
|
|
63
|
-
if (responseIds == null) {
|
|
64
|
-
responseIds = new Set();
|
|
65
|
-
entityToResponseIds.set(entityId, responseIds);
|
|
66
|
-
}
|
|
67
|
-
// typename:id => operation
|
|
68
|
-
responseIds.add(responseId);
|
|
69
|
-
// operation => typename:id
|
|
70
|
-
entityIds.add(entityId);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
get(responseId) {
|
|
75
|
-
var _a;
|
|
76
|
-
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
77
|
-
},
|
|
78
|
-
invalidate(entitiesToRemove) {
|
|
79
|
-
for (const { typename, id } of entitiesToRemove) {
|
|
80
|
-
purgeEntity(id != null ? buildEntityId(typename, id) : typename);
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
const defaultBuildEntityId = (typename, id) => `${typename}:${id}`;
|
|
86
|
-
|
|
87
|
-
const webCrypto = globalThis.crypto;
|
|
88
|
-
const sha256Browser = (bytes) => {
|
|
89
|
-
const hash = webCrypto.subtle.digest({ name: 'SHA-256' }, bytes);
|
|
90
|
-
return new Promise((resolve, reject) => {
|
|
91
|
-
Promise.resolve(hash)
|
|
92
|
-
.then(function (result) {
|
|
93
|
-
resolve(new Uint8Array(result));
|
|
94
|
-
})
|
|
95
|
-
.catch(function (error) {
|
|
96
|
-
reject(error);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
let nodeCrypto;
|
|
101
|
-
if (typeof window === 'undefined' && !webCrypto) {
|
|
102
|
-
// Indirect eval'd require/import to guarantee no side-effects in module scope
|
|
103
|
-
// (optimization for minifier)
|
|
104
|
-
try {
|
|
105
|
-
// eslint-disable-next-line no-new-func
|
|
106
|
-
nodeCrypto = Promise.resolve(new Function('require', 'return require("crypto")')(require));
|
|
107
|
-
}
|
|
108
|
-
catch (_error) {
|
|
109
|
-
try {
|
|
110
|
-
// eslint-disable-next-line no-new-func
|
|
111
|
-
nodeCrypto = new Function('return import("crypto")')();
|
|
112
|
-
}
|
|
113
|
-
catch (_error) { }
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
const hashSHA256 = async (text) => {
|
|
117
|
-
// Node.js support
|
|
118
|
-
if (nodeCrypto) {
|
|
119
|
-
return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
|
|
120
|
-
}
|
|
121
|
-
if (webCrypto) {
|
|
122
|
-
let buf;
|
|
123
|
-
if (typeof TextEncoder !== 'undefined') {
|
|
124
|
-
buf = new TextEncoder().encode(text);
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
buf = new Uint8Array(text.length);
|
|
128
|
-
for (let i = 0, l = text.length; i < l; i++) {
|
|
129
|
-
// NOTE: We assume that the input GraphQL Query only uses UTF-8 at most
|
|
130
|
-
// since GraphQL mostly consists of ASCII, this is completely fine
|
|
131
|
-
buf[i] = text.charCodeAt(i);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
const out = await sha256Browser(buf);
|
|
135
|
-
let hash = '';
|
|
136
|
-
for (let i = 0, l = out.length; i < l; i++) {
|
|
137
|
-
const hex = out[i].toString(16);
|
|
138
|
-
hash += '00'.slice(0, Math.max(0, 2 - hex.length)) + hex;
|
|
139
|
-
}
|
|
140
|
-
return hash;
|
|
141
|
-
}
|
|
142
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
143
|
-
// eslint-disable-next-line no-console
|
|
144
|
-
console.warn('[@envelop/response-cache]: The Node Crypto and Web Crypto APIs are not available.\n' +
|
|
145
|
-
'This is an unexpected error. Please report it by filing a GitHub Issue.');
|
|
146
|
-
}
|
|
147
|
-
return Promise.resolve('');
|
|
148
|
-
};
|
|
149
|
-
|
|
5
|
+
import { createInMemoryCache } from './in-memory-cache.js';
|
|
6
|
+
import { hashSHA256 } from './hashSHA256.js';
|
|
7
|
+
import { defaultGetDocumentString, useCacheDocumentString } from './cache-document-str.js';
|
|
150
8
|
const contextSymbol = Symbol('responseCache');
|
|
151
9
|
/**
|
|
152
10
|
* Default function used for building the response cache key.
|
|
153
11
|
* It is exported here for advanced use-cases. E.g. if you want to short circuit and serve responses from the cache on a global level in order to completely by-pass the GraphQL flow.
|
|
154
12
|
*/
|
|
155
|
-
const defaultBuildResponseCacheKey = params => {
|
|
13
|
+
export const defaultBuildResponseCacheKey = params => {
|
|
156
14
|
var _a, _b, _c;
|
|
157
15
|
return hashSHA256([
|
|
158
16
|
params.documentString,
|
|
@@ -169,7 +27,7 @@ const defaultBuildResponseCacheKey = params => {
|
|
|
169
27
|
*
|
|
170
28
|
* By default, results with errors (unexpected, EnvelopError, or GraphQLError) are not cached.
|
|
171
29
|
*/
|
|
172
|
-
const defaultShouldCacheResult = (params) => {
|
|
30
|
+
export const defaultShouldCacheResult = (params) => {
|
|
173
31
|
if (params.result.errors) {
|
|
174
32
|
// eslint-disable-next-line no-console
|
|
175
33
|
console.warn('[useResponseCache] Failed to cache due to errors');
|
|
@@ -177,16 +35,7 @@ const defaultShouldCacheResult = (params) => {
|
|
|
177
35
|
}
|
|
178
36
|
return true;
|
|
179
37
|
};
|
|
180
|
-
|
|
181
|
-
function defaultGetDocumentString(executionArgs) {
|
|
182
|
-
let documentString = documentStringByDocument.get(executionArgs.document);
|
|
183
|
-
if (!documentString) {
|
|
184
|
-
documentString = print(executionArgs.document);
|
|
185
|
-
documentStringByDocument.set(executionArgs.document, documentString);
|
|
186
|
-
}
|
|
187
|
-
return documentString;
|
|
188
|
-
}
|
|
189
|
-
function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl = Infinity, session = () => null, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = defaultBuildResponseCacheKey, getDocumentString = defaultGetDocumentString, shouldCacheResult = defaultShouldCacheResult,
|
|
38
|
+
export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl = Infinity, session = () => null, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = defaultBuildResponseCacheKey, getDocumentString = defaultGetDocumentString, shouldCacheResult = defaultShouldCacheResult,
|
|
190
39
|
// eslint-disable-next-line dot-notation
|
|
191
40
|
includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false, } = {}) {
|
|
192
41
|
const appliedTransform = Symbol('responseCache.appliedTransform');
|
|
@@ -194,6 +43,11 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
194
43
|
// never cache Introspections
|
|
195
44
|
ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
|
|
196
45
|
return {
|
|
46
|
+
onPluginInit({ addPlugin }) {
|
|
47
|
+
if (getDocumentString === defaultGetDocumentString) {
|
|
48
|
+
addPlugin(useCacheDocumentString());
|
|
49
|
+
}
|
|
50
|
+
},
|
|
197
51
|
onSchemaChange({ schema, replaceSchema }) {
|
|
198
52
|
var _a;
|
|
199
53
|
// @ts-expect-error See https://github.com/graphql/graphql-js/pull/3511 - remove this comments once merged
|
|
@@ -207,13 +61,6 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
207
61
|
};
|
|
208
62
|
replaceSchema(patchedSchema);
|
|
209
63
|
},
|
|
210
|
-
onParse({ params: { source } }) {
|
|
211
|
-
return function onParseEnd({ result }) {
|
|
212
|
-
if (result != null && !(result instanceof Error)) {
|
|
213
|
-
documentStringByDocument.set(result, source.toString());
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
},
|
|
217
64
|
async onExecute(ctx) {
|
|
218
65
|
var _a;
|
|
219
66
|
const identifier = new Map();
|
|
@@ -257,8 +104,9 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
257
104
|
}
|
|
258
105
|
const operationId = await buildResponseCacheKey({
|
|
259
106
|
documentString: getDocumentString(ctx.args),
|
|
107
|
+
variableValues: ctx.args.variableValues,
|
|
108
|
+
operationName: ctx.args.operationName,
|
|
260
109
|
sessionId: session(ctx.args.contextValue),
|
|
261
|
-
...ctx.args,
|
|
262
110
|
});
|
|
263
111
|
if (((_a = enabled === null || enabled === void 0 ? void 0 : enabled(ctx.args.contextValue)) !== null && _a !== void 0 ? _a : true) === true) {
|
|
264
112
|
const cachedResponse = await cache.get(operationId);
|
|
@@ -398,5 +246,3 @@ function applyResponseCacheLogic(schema, idFieldNames) {
|
|
|
398
246
|
},
|
|
399
247
|
});
|
|
400
248
|
}
|
|
401
|
-
|
|
402
|
-
export { createInMemoryCache, defaultBuildEntityId, defaultBuildResponseCacheKey, defaultGetDocumentString, defaultShouldCacheResult, useResponseCache };
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/response-cache",
|
|
3
|
-
"version": "3.0.0-alpha-
|
|
3
|
+
"version": "3.0.0-alpha-e8a9318.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@envelop/core": "^2.
|
|
6
|
+
"@envelop/core": "^2.4.0",
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@graphql-tools/utils": "^8.
|
|
11
|
-
"
|
|
12
|
-
"
|
|
10
|
+
"@graphql-tools/utils": "^8.8.0",
|
|
11
|
+
"lru-cache": "^6.0.0",
|
|
12
|
+
"fast-json-stable-stringify": "^2.1.0"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -18,21 +18,42 @@
|
|
|
18
18
|
},
|
|
19
19
|
"author": "Kamil Kisiela <kamilkisiela@gmail.com>",
|
|
20
20
|
"license": "MIT",
|
|
21
|
-
"main": "index.js",
|
|
22
|
-
"module": "index.
|
|
23
|
-
"typings": "index.d.ts",
|
|
21
|
+
"main": "cjs/index.js",
|
|
22
|
+
"module": "esm/index.js",
|
|
23
|
+
"typings": "typings/index.d.ts",
|
|
24
24
|
"typescript": {
|
|
25
|
-
"definition": "index.d.ts"
|
|
25
|
+
"definition": "typings/index.d.ts"
|
|
26
26
|
},
|
|
27
|
+
"type": "module",
|
|
27
28
|
"exports": {
|
|
28
29
|
".": {
|
|
29
|
-
"require":
|
|
30
|
-
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./typings/index.d.ts",
|
|
32
|
+
"default": "./cjs/index.js"
|
|
33
|
+
},
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./typings/index.d.ts",
|
|
36
|
+
"default": "./esm/index.js"
|
|
37
|
+
},
|
|
38
|
+
"default": {
|
|
39
|
+
"types": "./typings/index.d.ts",
|
|
40
|
+
"default": "./esm/index.js"
|
|
41
|
+
}
|
|
31
42
|
},
|
|
32
43
|
"./*": {
|
|
33
|
-
"require":
|
|
34
|
-
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./typings/*.d.ts",
|
|
46
|
+
"default": "./cjs/*.js"
|
|
47
|
+
},
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./typings/*.d.ts",
|
|
50
|
+
"default": "./esm/*.js"
|
|
51
|
+
},
|
|
52
|
+
"default": {
|
|
53
|
+
"types": "./typings/*.d.ts",
|
|
54
|
+
"default": "./esm/*.js"
|
|
55
|
+
}
|
|
35
56
|
},
|
|
36
57
|
"./package.json": "./package.json"
|
|
37
58
|
}
|
|
38
|
-
}
|
|
59
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Cache } from './cache';
|
|
1
|
+
import type { Cache } from './cache.js';
|
|
2
2
|
export declare type BuildEntityId = (typename: string, id: number | string) => string;
|
|
3
3
|
export declare type InMemoryCacheParameter = {
|
|
4
4
|
/**
|
|
@@ -11,5 +11,5 @@ export declare type InMemoryCacheParameter = {
|
|
|
11
11
|
*/
|
|
12
12
|
buildEntityId?: BuildEntityId;
|
|
13
13
|
};
|
|
14
|
-
export declare const createInMemoryCache: (params?: InMemoryCacheParameter
|
|
14
|
+
export declare const createInMemoryCache: (params?: InMemoryCacheParameter) => Cache;
|
|
15
15
|
export declare const defaultBuildEntityId: BuildEntityId;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { Plugin, Maybe } from '@envelop/core';
|
|
2
2
|
import { ExecutionArgs, ExecutionResult } from 'graphql';
|
|
3
|
-
import type { Cache } from './cache';
|
|
3
|
+
import type { Cache } from './cache.js';
|
|
4
4
|
/**
|
|
5
5
|
* Function for building the response cache key based on the input parameters
|
|
6
6
|
*/
|
|
7
7
|
export declare type BuildResponseCacheKeyFunction = (params: {
|
|
8
8
|
/** Raw document string as sent from the client. */
|
|
9
9
|
documentString: string;
|
|
10
|
+
/** Variable values as sent form the client. */
|
|
11
|
+
variableValues: ExecutionArgs['variableValues'];
|
|
12
|
+
/** The name of the GraphQL operation that should be executed from within the document. */
|
|
13
|
+
operationName?: Maybe<string>;
|
|
10
14
|
/** optional sessionId for make unique cache keys based on the session. */
|
|
11
15
|
sessionId?: Maybe<string>;
|
|
12
|
-
}
|
|
16
|
+
}) => Promise<string>;
|
|
13
17
|
export declare type GetDocumentStringFunction = (executionArgs: ExecutionArgs) => string;
|
|
14
18
|
export declare type ShouldCacheResultFunction = (params: {
|
|
15
19
|
result: ExecutionResult;
|
|
@@ -101,5 +105,4 @@ export declare const defaultBuildResponseCacheKey: BuildResponseCacheKeyFunction
|
|
|
101
105
|
* By default, results with errors (unexpected, EnvelopError, or GraphQLError) are not cached.
|
|
102
106
|
*/
|
|
103
107
|
export declare const defaultShouldCacheResult: ShouldCacheResultFunction;
|
|
104
|
-
export declare function defaultGetDocumentString(executionArgs: ExecutionArgs): string;
|
|
105
108
|
export declare function useResponseCache({ cache, ttl: globalTtl, session, enabled, ignoredTypes, ttlPerType, ttlPerSchemaCoordinate, idFields, invalidateViaMutation, buildResponseCacheKey, getDocumentString, shouldCacheResult, includeExtensionMetadata, }?: UseResponseCacheParameter): Plugin;
|
package/index.d.ts
DELETED