@envelop/response-cache 3.0.0-alpha-d0d0776.0 → 3.0.0-alpha-6a4e0e2.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/index.js +111 -106
- package/index.mjs +107 -99
- package/package.json +6 -18
- package/plugin.d.ts +10 -13
package/index.js
CHANGED
|
@@ -2,21 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
6
|
+
|
|
7
|
+
const LRU = _interopDefault(require('lru-cache'));
|
|
6
8
|
const core = require('@envelop/core');
|
|
7
9
|
const utils = require('@graphql-tools/utils');
|
|
8
10
|
const graphql = require('graphql');
|
|
9
|
-
const jsonStableStringify = require('fast-json-stable-stringify');
|
|
10
|
-
|
|
11
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
12
|
-
|
|
13
|
-
const LRU__default = /*#__PURE__*/_interopDefaultLegacy(LRU);
|
|
14
|
-
const jsonStableStringify__default = /*#__PURE__*/_interopDefaultLegacy(jsonStableStringify);
|
|
11
|
+
const jsonStableStringify = _interopDefault(require('fast-json-stable-stringify'));
|
|
15
12
|
|
|
16
13
|
const createInMemoryCache = (params) => {
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
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,
|
|
20
18
|
stale: false,
|
|
21
19
|
noDisposeOnSet: true,
|
|
22
20
|
dispose(responseId) {
|
|
@@ -26,12 +24,13 @@ const createInMemoryCache = (params) => {
|
|
|
26
24
|
const entityToResponseIds = new Map();
|
|
27
25
|
const responseIdToEntityIds = new Map();
|
|
28
26
|
function purgeResponse(responseId, shouldRemove = true) {
|
|
27
|
+
var _a;
|
|
29
28
|
const entityIds = responseIdToEntityIds.get(responseId);
|
|
30
29
|
// get entities related to the response
|
|
31
30
|
if (entityIds !== undefined) {
|
|
32
31
|
for (const entityId of entityIds) {
|
|
33
32
|
// remove the response mapping from the entity
|
|
34
|
-
entityToResponseIds.get(entityId)
|
|
33
|
+
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
35
34
|
}
|
|
36
35
|
// remove all the entity mappings from the response
|
|
37
36
|
responseIdToEntityIds.delete(responseId);
|
|
@@ -79,7 +78,8 @@ const createInMemoryCache = (params) => {
|
|
|
79
78
|
}
|
|
80
79
|
},
|
|
81
80
|
get(responseId) {
|
|
82
|
-
|
|
81
|
+
var _a;
|
|
82
|
+
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
83
83
|
},
|
|
84
84
|
invalidate(entitiesToRemove) {
|
|
85
85
|
for (const { typename, id } of entitiesToRemove) {
|
|
@@ -154,17 +154,19 @@ const hashSHA256 = async (text) => {
|
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
const contextSymbol = Symbol('responseCache');
|
|
157
|
-
const rawDocumentStringSymbol = Symbol('rawDocumentString');
|
|
158
157
|
/**
|
|
159
158
|
* Default function used for building the response cache key.
|
|
160
159
|
* 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.
|
|
161
160
|
*/
|
|
162
|
-
const defaultBuildResponseCacheKey = params =>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
const defaultBuildResponseCacheKey = params => {
|
|
162
|
+
var _a, _b, _c;
|
|
163
|
+
return hashSHA256([
|
|
164
|
+
params.documentString,
|
|
165
|
+
(_a = params.operationName) !== null && _a !== void 0 ? _a : '',
|
|
166
|
+
jsonStableStringify((_b = params.variableValues) !== null && _b !== void 0 ? _b : {}),
|
|
167
|
+
(_c = params.sessionId) !== null && _c !== void 0 ? _c : '',
|
|
168
|
+
].join('|'));
|
|
169
|
+
};
|
|
168
170
|
/**
|
|
169
171
|
* Default function used to check if the result should be cached.
|
|
170
172
|
*
|
|
@@ -181,8 +183,16 @@ const defaultShouldCacheResult = (params) => {
|
|
|
181
183
|
}
|
|
182
184
|
return true;
|
|
183
185
|
};
|
|
184
|
-
const
|
|
185
|
-
function
|
|
186
|
+
const documentStringByDocument = new WeakMap();
|
|
187
|
+
function defaultGetDocumentString(executionArgs) {
|
|
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,
|
|
186
196
|
// eslint-disable-next-line dot-notation
|
|
187
197
|
includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false, } = {}) {
|
|
188
198
|
const appliedTransform = Symbol('responseCache.appliedTransform');
|
|
@@ -191,8 +201,9 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
191
201
|
ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
|
|
192
202
|
return {
|
|
193
203
|
onSchemaChange({ schema, replaceSchema }) {
|
|
204
|
+
var _a;
|
|
194
205
|
// @ts-expect-error See https://github.com/graphql/graphql-js/pull/3511 - remove this comments once merged
|
|
195
|
-
if (schema.extensions
|
|
206
|
+
if (((_a = schema.extensions) === null || _a === void 0 ? void 0 : _a[appliedTransform]) === true) {
|
|
196
207
|
return;
|
|
197
208
|
}
|
|
198
209
|
const patchedSchema = applyResponseCacheLogic(schema, idFields);
|
|
@@ -202,18 +213,15 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
202
213
|
};
|
|
203
214
|
replaceSchema(patchedSchema);
|
|
204
215
|
},
|
|
205
|
-
onParse(
|
|
206
|
-
return function onParseEnd(
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
const rawDocumentString = typeof source === 'string' ? source : source.body;
|
|
210
|
-
ctx.extendContext({
|
|
211
|
-
[rawDocumentStringSymbol]: rawDocumentString,
|
|
212
|
-
});
|
|
216
|
+
onParse({ params: { source } }) {
|
|
217
|
+
return function onParseEnd({ result }) {
|
|
218
|
+
if (result != null && !(result instanceof Error)) {
|
|
219
|
+
documentStringByDocument.set(result, source.toString());
|
|
213
220
|
}
|
|
214
221
|
};
|
|
215
222
|
},
|
|
216
223
|
async onExecute(ctx) {
|
|
224
|
+
var _a;
|
|
217
225
|
const identifier = new Map();
|
|
218
226
|
const types = new Set();
|
|
219
227
|
const context = {
|
|
@@ -253,99 +261,95 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
253
261
|
},
|
|
254
262
|
};
|
|
255
263
|
}
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
extensions: {
|
|
271
|
-
responseCache: {
|
|
272
|
-
hit: true,
|
|
273
|
-
},
|
|
264
|
+
const operationId = await buildResponseCacheKey({
|
|
265
|
+
documentString: getDocumentString(ctx.args),
|
|
266
|
+
sessionId: session(ctx.args.contextValue),
|
|
267
|
+
...ctx.args,
|
|
268
|
+
});
|
|
269
|
+
if (((_a = enabled === null || enabled === void 0 ? void 0 : enabled(ctx.args.contextValue)) !== null && _a !== void 0 ? _a : true) === true) {
|
|
270
|
+
const cachedResponse = await cache.get(operationId);
|
|
271
|
+
if (cachedResponse != null) {
|
|
272
|
+
if (includeExtensionMetadata) {
|
|
273
|
+
ctx.setResultAndStopExecution({
|
|
274
|
+
...cachedResponse,
|
|
275
|
+
extensions: {
|
|
276
|
+
responseCache: {
|
|
277
|
+
hit: true,
|
|
274
278
|
},
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
ctx.setResultAndStopExecution(cachedResponse);
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
281
|
}
|
|
282
|
+
else {
|
|
283
|
+
ctx.setResultAndStopExecution(cachedResponse);
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
282
286
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
},
|
|
296
|
-
}));
|
|
297
|
-
}
|
|
298
|
-
return {
|
|
299
|
-
onExecuteDone({ result, setResult }) {
|
|
300
|
-
if (core.isAsyncIterable(result)) {
|
|
301
|
-
// eslint-disable-next-line no-console
|
|
302
|
-
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
if (!shouldCacheResult({ result })) {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
// we only use the global ttl if no currentTtl has been determined.
|
|
309
|
-
const finalTtl = context.currentTtl ?? globalTtl;
|
|
310
|
-
if (finalTtl === 0) {
|
|
311
|
-
if (includeExtensionMetadata) {
|
|
312
|
-
setResult({
|
|
313
|
-
...result,
|
|
314
|
-
extensions: {
|
|
315
|
-
responseCache: {
|
|
316
|
-
hit: false,
|
|
317
|
-
didCache: false,
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
});
|
|
287
|
+
}
|
|
288
|
+
if (ttlPerSchemaCoordinate) {
|
|
289
|
+
const typeInfo = new graphql.TypeInfo(ctx.args.schema);
|
|
290
|
+
graphql.visit(ctx.args.document, graphql.visitWithTypeInfo(typeInfo, {
|
|
291
|
+
Field(fieldNode) {
|
|
292
|
+
const parentType = typeInfo.getParentType();
|
|
293
|
+
if (parentType) {
|
|
294
|
+
const schemaCoordinate = `${parentType.name}.${fieldNode.name.value}`;
|
|
295
|
+
const maybeTtl = ttlPerSchemaCoordinate[schemaCoordinate];
|
|
296
|
+
if (maybeTtl !== undefined) {
|
|
297
|
+
context.currentTtl = calculateTtl(maybeTtl, context.currentTtl);
|
|
321
298
|
}
|
|
322
|
-
return;
|
|
323
299
|
}
|
|
324
|
-
|
|
300
|
+
},
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
onExecuteDone({ result, setResult }) {
|
|
305
|
+
var _a;
|
|
306
|
+
if (core.isAsyncIterable(result)) {
|
|
307
|
+
// eslint-disable-next-line no-console
|
|
308
|
+
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (context.skip) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (!shouldCacheResult({ result })) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
// we only use the global ttl if no currentTtl has been determined.
|
|
318
|
+
const finalTtl = (_a = context.currentTtl) !== null && _a !== void 0 ? _a : globalTtl;
|
|
319
|
+
if (finalTtl === 0) {
|
|
325
320
|
if (includeExtensionMetadata) {
|
|
326
321
|
setResult({
|
|
327
322
|
...result,
|
|
328
323
|
extensions: {
|
|
329
324
|
responseCache: {
|
|
330
325
|
hit: false,
|
|
331
|
-
didCache:
|
|
332
|
-
ttl: finalTtl,
|
|
326
|
+
didCache: false,
|
|
333
327
|
},
|
|
334
328
|
},
|
|
335
329
|
});
|
|
336
330
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
cache.set(operationId, result, identifier.values(), finalTtl);
|
|
334
|
+
if (includeExtensionMetadata) {
|
|
335
|
+
setResult({
|
|
336
|
+
...result,
|
|
337
|
+
extensions: {
|
|
338
|
+
responseCache: {
|
|
339
|
+
hit: false,
|
|
340
|
+
didCache: true,
|
|
341
|
+
ttl: finalTtl,
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
};
|
|
344
348
|
},
|
|
345
349
|
};
|
|
346
350
|
}
|
|
347
351
|
function isOperationDefinition(node) {
|
|
348
|
-
return node
|
|
352
|
+
return (node === null || node === void 0 ? void 0 : node.kind) === 'OperationDefinition';
|
|
349
353
|
}
|
|
350
354
|
function calculateTtl(typeTtl, currentTtl) {
|
|
351
355
|
if (typeof currentTtl === 'number') {
|
|
@@ -371,7 +375,8 @@ function applyResponseCacheLogic(schema, idFieldNames) {
|
|
|
371
375
|
return {
|
|
372
376
|
...fieldConfig,
|
|
373
377
|
resolve(src, args, context, info) {
|
|
374
|
-
|
|
378
|
+
var _a;
|
|
379
|
+
const result = ((_a = fieldConfig.resolve) !== null && _a !== void 0 ? _a : graphql.defaultFieldResolver)(src, args, context, info);
|
|
375
380
|
runWith(result, (id) => {
|
|
376
381
|
const ctx = context[contextSymbol];
|
|
377
382
|
if (ctx !== undefined) {
|
|
@@ -403,6 +408,6 @@ function applyResponseCacheLogic(schema, idFieldNames) {
|
|
|
403
408
|
exports.createInMemoryCache = createInMemoryCache;
|
|
404
409
|
exports.defaultBuildEntityId = defaultBuildEntityId;
|
|
405
410
|
exports.defaultBuildResponseCacheKey = defaultBuildResponseCacheKey;
|
|
406
|
-
exports.
|
|
411
|
+
exports.defaultGetDocumentString = defaultGetDocumentString;
|
|
407
412
|
exports.defaultShouldCacheResult = defaultShouldCacheResult;
|
|
408
413
|
exports.useResponseCache = useResponseCache;
|
package/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import LRU from 'lru-cache';
|
|
2
2
|
import { isAsyncIterable } from '@envelop/core';
|
|
3
3
|
import { mapSchema, MapperKind } from '@graphql-tools/utils';
|
|
4
|
-
import { defaultFieldResolver, TypeInfo, visit, visitWithTypeInfo } from 'graphql';
|
|
4
|
+
import { print, defaultFieldResolver, TypeInfo, visit, visitWithTypeInfo } from 'graphql';
|
|
5
5
|
import jsonStableStringify from 'fast-json-stable-stringify';
|
|
6
6
|
|
|
7
7
|
const createInMemoryCache = (params) => {
|
|
8
|
-
|
|
8
|
+
var _a;
|
|
9
|
+
const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : defaultBuildEntityId;
|
|
9
10
|
const cachedResponses = new LRU({
|
|
10
|
-
max: params
|
|
11
|
+
max: params === null || params === void 0 ? void 0 : params.max,
|
|
11
12
|
stale: false,
|
|
12
13
|
noDisposeOnSet: true,
|
|
13
14
|
dispose(responseId) {
|
|
@@ -17,12 +18,13 @@ const createInMemoryCache = (params) => {
|
|
|
17
18
|
const entityToResponseIds = new Map();
|
|
18
19
|
const responseIdToEntityIds = new Map();
|
|
19
20
|
function purgeResponse(responseId, shouldRemove = true) {
|
|
21
|
+
var _a;
|
|
20
22
|
const entityIds = responseIdToEntityIds.get(responseId);
|
|
21
23
|
// get entities related to the response
|
|
22
24
|
if (entityIds !== undefined) {
|
|
23
25
|
for (const entityId of entityIds) {
|
|
24
26
|
// remove the response mapping from the entity
|
|
25
|
-
entityToResponseIds.get(entityId)
|
|
27
|
+
(_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
|
|
26
28
|
}
|
|
27
29
|
// remove all the entity mappings from the response
|
|
28
30
|
responseIdToEntityIds.delete(responseId);
|
|
@@ -70,7 +72,8 @@ const createInMemoryCache = (params) => {
|
|
|
70
72
|
}
|
|
71
73
|
},
|
|
72
74
|
get(responseId) {
|
|
73
|
-
|
|
75
|
+
var _a;
|
|
76
|
+
return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
|
|
74
77
|
},
|
|
75
78
|
invalidate(entitiesToRemove) {
|
|
76
79
|
for (const { typename, id } of entitiesToRemove) {
|
|
@@ -145,17 +148,19 @@ const hashSHA256 = async (text) => {
|
|
|
145
148
|
};
|
|
146
149
|
|
|
147
150
|
const contextSymbol = Symbol('responseCache');
|
|
148
|
-
const rawDocumentStringSymbol = Symbol('rawDocumentString');
|
|
149
151
|
/**
|
|
150
152
|
* Default function used for building the response cache key.
|
|
151
153
|
* 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.
|
|
152
154
|
*/
|
|
153
|
-
const defaultBuildResponseCacheKey = params =>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
const defaultBuildResponseCacheKey = params => {
|
|
156
|
+
var _a, _b, _c;
|
|
157
|
+
return hashSHA256([
|
|
158
|
+
params.documentString,
|
|
159
|
+
(_a = params.operationName) !== null && _a !== void 0 ? _a : '',
|
|
160
|
+
jsonStableStringify((_b = params.variableValues) !== null && _b !== void 0 ? _b : {}),
|
|
161
|
+
(_c = params.sessionId) !== null && _c !== void 0 ? _c : '',
|
|
162
|
+
].join('|'));
|
|
163
|
+
};
|
|
159
164
|
/**
|
|
160
165
|
* Default function used to check if the result should be cached.
|
|
161
166
|
*
|
|
@@ -172,8 +177,16 @@ const defaultShouldCacheResult = (params) => {
|
|
|
172
177
|
}
|
|
173
178
|
return true;
|
|
174
179
|
};
|
|
175
|
-
const
|
|
176
|
-
function
|
|
180
|
+
const documentStringByDocument = new WeakMap();
|
|
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,
|
|
177
190
|
// eslint-disable-next-line dot-notation
|
|
178
191
|
includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false, } = {}) {
|
|
179
192
|
const appliedTransform = Symbol('responseCache.appliedTransform');
|
|
@@ -182,8 +195,9 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
182
195
|
ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
|
|
183
196
|
return {
|
|
184
197
|
onSchemaChange({ schema, replaceSchema }) {
|
|
198
|
+
var _a;
|
|
185
199
|
// @ts-expect-error See https://github.com/graphql/graphql-js/pull/3511 - remove this comments once merged
|
|
186
|
-
if (schema.extensions
|
|
200
|
+
if (((_a = schema.extensions) === null || _a === void 0 ? void 0 : _a[appliedTransform]) === true) {
|
|
187
201
|
return;
|
|
188
202
|
}
|
|
189
203
|
const patchedSchema = applyResponseCacheLogic(schema, idFields);
|
|
@@ -193,18 +207,15 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
193
207
|
};
|
|
194
208
|
replaceSchema(patchedSchema);
|
|
195
209
|
},
|
|
196
|
-
onParse(
|
|
197
|
-
return function onParseEnd(
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
const rawDocumentString = typeof source === 'string' ? source : source.body;
|
|
201
|
-
ctx.extendContext({
|
|
202
|
-
[rawDocumentStringSymbol]: rawDocumentString,
|
|
203
|
-
});
|
|
210
|
+
onParse({ params: { source } }) {
|
|
211
|
+
return function onParseEnd({ result }) {
|
|
212
|
+
if (result != null && !(result instanceof Error)) {
|
|
213
|
+
documentStringByDocument.set(result, source.toString());
|
|
204
214
|
}
|
|
205
215
|
};
|
|
206
216
|
},
|
|
207
217
|
async onExecute(ctx) {
|
|
218
|
+
var _a;
|
|
208
219
|
const identifier = new Map();
|
|
209
220
|
const types = new Set();
|
|
210
221
|
const context = {
|
|
@@ -244,99 +255,95 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
|
|
|
244
255
|
},
|
|
245
256
|
};
|
|
246
257
|
}
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
extensions: {
|
|
262
|
-
responseCache: {
|
|
263
|
-
hit: true,
|
|
264
|
-
},
|
|
258
|
+
const operationId = await buildResponseCacheKey({
|
|
259
|
+
documentString: getDocumentString(ctx.args),
|
|
260
|
+
sessionId: session(ctx.args.contextValue),
|
|
261
|
+
...ctx.args,
|
|
262
|
+
});
|
|
263
|
+
if (((_a = enabled === null || enabled === void 0 ? void 0 : enabled(ctx.args.contextValue)) !== null && _a !== void 0 ? _a : true) === true) {
|
|
264
|
+
const cachedResponse = await cache.get(operationId);
|
|
265
|
+
if (cachedResponse != null) {
|
|
266
|
+
if (includeExtensionMetadata) {
|
|
267
|
+
ctx.setResultAndStopExecution({
|
|
268
|
+
...cachedResponse,
|
|
269
|
+
extensions: {
|
|
270
|
+
responseCache: {
|
|
271
|
+
hit: true,
|
|
265
272
|
},
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
ctx.setResultAndStopExecution(cachedResponse);
|
|
270
|
-
}
|
|
271
|
-
return;
|
|
273
|
+
},
|
|
274
|
+
});
|
|
272
275
|
}
|
|
276
|
+
else {
|
|
277
|
+
ctx.setResultAndStopExecution(cachedResponse);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
273
280
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
},
|
|
287
|
-
}));
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
onExecuteDone({ result, setResult }) {
|
|
291
|
-
if (isAsyncIterable(result)) {
|
|
292
|
-
// eslint-disable-next-line no-console
|
|
293
|
-
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
if (!shouldCacheResult({ result })) {
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
// we only use the global ttl if no currentTtl has been determined.
|
|
300
|
-
const finalTtl = context.currentTtl ?? globalTtl;
|
|
301
|
-
if (finalTtl === 0) {
|
|
302
|
-
if (includeExtensionMetadata) {
|
|
303
|
-
setResult({
|
|
304
|
-
...result,
|
|
305
|
-
extensions: {
|
|
306
|
-
responseCache: {
|
|
307
|
-
hit: false,
|
|
308
|
-
didCache: false,
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
});
|
|
281
|
+
}
|
|
282
|
+
if (ttlPerSchemaCoordinate) {
|
|
283
|
+
const typeInfo = new TypeInfo(ctx.args.schema);
|
|
284
|
+
visit(ctx.args.document, visitWithTypeInfo(typeInfo, {
|
|
285
|
+
Field(fieldNode) {
|
|
286
|
+
const parentType = typeInfo.getParentType();
|
|
287
|
+
if (parentType) {
|
|
288
|
+
const schemaCoordinate = `${parentType.name}.${fieldNode.name.value}`;
|
|
289
|
+
const maybeTtl = ttlPerSchemaCoordinate[schemaCoordinate];
|
|
290
|
+
if (maybeTtl !== undefined) {
|
|
291
|
+
context.currentTtl = calculateTtl(maybeTtl, context.currentTtl);
|
|
312
292
|
}
|
|
313
|
-
return;
|
|
314
293
|
}
|
|
315
|
-
|
|
294
|
+
},
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
onExecuteDone({ result, setResult }) {
|
|
299
|
+
var _a;
|
|
300
|
+
if (isAsyncIterable(result)) {
|
|
301
|
+
// eslint-disable-next-line no-console
|
|
302
|
+
console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (context.skip) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (!shouldCacheResult({ result })) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
// we only use the global ttl if no currentTtl has been determined.
|
|
312
|
+
const finalTtl = (_a = context.currentTtl) !== null && _a !== void 0 ? _a : globalTtl;
|
|
313
|
+
if (finalTtl === 0) {
|
|
316
314
|
if (includeExtensionMetadata) {
|
|
317
315
|
setResult({
|
|
318
316
|
...result,
|
|
319
317
|
extensions: {
|
|
320
318
|
responseCache: {
|
|
321
319
|
hit: false,
|
|
322
|
-
didCache:
|
|
323
|
-
ttl: finalTtl,
|
|
320
|
+
didCache: false,
|
|
324
321
|
},
|
|
325
322
|
},
|
|
326
323
|
});
|
|
327
324
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
cache.set(operationId, result, identifier.values(), finalTtl);
|
|
328
|
+
if (includeExtensionMetadata) {
|
|
329
|
+
setResult({
|
|
330
|
+
...result,
|
|
331
|
+
extensions: {
|
|
332
|
+
responseCache: {
|
|
333
|
+
hit: false,
|
|
334
|
+
didCache: true,
|
|
335
|
+
ttl: finalTtl,
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
};
|
|
335
342
|
},
|
|
336
343
|
};
|
|
337
344
|
}
|
|
338
345
|
function isOperationDefinition(node) {
|
|
339
|
-
return node
|
|
346
|
+
return (node === null || node === void 0 ? void 0 : node.kind) === 'OperationDefinition';
|
|
340
347
|
}
|
|
341
348
|
function calculateTtl(typeTtl, currentTtl) {
|
|
342
349
|
if (typeof currentTtl === 'number') {
|
|
@@ -362,7 +369,8 @@ function applyResponseCacheLogic(schema, idFieldNames) {
|
|
|
362
369
|
return {
|
|
363
370
|
...fieldConfig,
|
|
364
371
|
resolve(src, args, context, info) {
|
|
365
|
-
|
|
372
|
+
var _a;
|
|
373
|
+
const result = ((_a = fieldConfig.resolve) !== null && _a !== void 0 ? _a : defaultFieldResolver)(src, args, context, info);
|
|
366
374
|
runWith(result, (id) => {
|
|
367
375
|
const ctx = context[contextSymbol];
|
|
368
376
|
if (ctx !== undefined) {
|
|
@@ -391,4 +399,4 @@ function applyResponseCacheLogic(schema, idFieldNames) {
|
|
|
391
399
|
});
|
|
392
400
|
}
|
|
393
401
|
|
|
394
|
-
export { createInMemoryCache, defaultBuildEntityId, defaultBuildResponseCacheKey,
|
|
402
|
+
export { createInMemoryCache, defaultBuildEntityId, defaultBuildResponseCacheKey, defaultGetDocumentString, defaultShouldCacheResult, useResponseCache };
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/response-cache",
|
|
3
|
-
"version": "3.0.0-alpha-
|
|
3
|
+
"version": "3.0.0-alpha-6a4e0e2.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@envelop/core": "3.
|
|
6
|
+
"@envelop/core": "^2.3.3",
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
@@ -26,24 +26,12 @@
|
|
|
26
26
|
},
|
|
27
27
|
"exports": {
|
|
28
28
|
".": {
|
|
29
|
-
"require":
|
|
30
|
-
|
|
31
|
-
"types": "./index.d.ts"
|
|
32
|
-
},
|
|
33
|
-
"import": {
|
|
34
|
-
"default": "./index.mjs",
|
|
35
|
-
"types": "./index.d.ts"
|
|
36
|
-
}
|
|
29
|
+
"require": "./index.js",
|
|
30
|
+
"import": "./index.mjs"
|
|
37
31
|
},
|
|
38
32
|
"./*": {
|
|
39
|
-
"require":
|
|
40
|
-
|
|
41
|
-
"types": "./*.d.ts"
|
|
42
|
-
},
|
|
43
|
-
"import": {
|
|
44
|
-
"default": "./*.mjs",
|
|
45
|
-
"types": "./*.d.ts"
|
|
46
|
-
}
|
|
33
|
+
"require": "./*.js",
|
|
34
|
+
"import": "./*.mjs"
|
|
47
35
|
},
|
|
48
36
|
"./package.json": "./package.json"
|
|
49
37
|
}
|
package/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Plugin, Maybe
|
|
1
|
+
import { Plugin, Maybe } from '@envelop/core';
|
|
2
2
|
import { ExecutionArgs, ExecutionResult } from 'graphql';
|
|
3
3
|
import type { Cache } from './cache';
|
|
4
4
|
/**
|
|
@@ -7,14 +7,10 @@ import type { Cache } from './cache';
|
|
|
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>;
|
|
14
10
|
/** optional sessionId for make unique cache keys based on the session. */
|
|
15
11
|
sessionId?: Maybe<string>;
|
|
16
|
-
}) => Promise<string>;
|
|
17
|
-
export declare type
|
|
12
|
+
} & ExecutionArgs) => Promise<string>;
|
|
13
|
+
export declare type GetDocumentStringFunction = (executionArgs: ExecutionArgs) => string;
|
|
18
14
|
export declare type ShouldCacheResultFunction = (params: {
|
|
19
15
|
result: ExecutionResult;
|
|
20
16
|
}) => Boolean;
|
|
@@ -72,12 +68,13 @@ export declare type UseResponseCacheParameter<C = any> = {
|
|
|
72
68
|
*/
|
|
73
69
|
buildResponseCacheKey?: BuildResponseCacheKeyFunction;
|
|
74
70
|
/**
|
|
75
|
-
* Function used for reading the document string that is used for building the response cache key from the
|
|
76
|
-
* By default, the useResponseCache plugin hooks into onParse and
|
|
71
|
+
* Function used for reading the document string that is used for building the response cache key from the execution arguments.
|
|
72
|
+
* By default, the useResponseCache plugin hooks into onParse and caches the original operation string in a WeakMap.
|
|
77
73
|
* If you are hard overriding parse you need to set this function, otherwise responses will not be cached or served from the cache.
|
|
78
|
-
* Defaults to `
|
|
74
|
+
* Defaults to `defaultGetDocumentString`
|
|
75
|
+
*
|
|
79
76
|
*/
|
|
80
|
-
|
|
77
|
+
getDocumentString?: GetDocumentStringFunction;
|
|
81
78
|
/**
|
|
82
79
|
* Include extension values that provide useful information, such as whether the cache was hit or which resources a mutation invalidated.
|
|
83
80
|
* Defaults to `true` if `process.env["NODE_ENV"]` is set to `"development"`, otherwise `false`.
|
|
@@ -104,5 +101,5 @@ export declare const defaultBuildResponseCacheKey: BuildResponseCacheKeyFunction
|
|
|
104
101
|
* By default, results with errors (unexpected, EnvelopError, or GraphQLError) are not cached.
|
|
105
102
|
*/
|
|
106
103
|
export declare const defaultShouldCacheResult: ShouldCacheResultFunction;
|
|
107
|
-
export declare
|
|
108
|
-
export declare function useResponseCache({ cache, ttl: globalTtl, session, enabled, ignoredTypes, ttlPerType, ttlPerSchemaCoordinate, idFields, invalidateViaMutation, buildResponseCacheKey,
|
|
104
|
+
export declare function defaultGetDocumentString(executionArgs: ExecutionArgs): string;
|
|
105
|
+
export declare function useResponseCache({ cache, ttl: globalTtl, session, enabled, ignoredTypes, ttlPerType, ttlPerSchemaCoordinate, idFields, invalidateViaMutation, buildResponseCacheKey, getDocumentString, shouldCacheResult, includeExtensionMetadata, }?: UseResponseCacheParameter): Plugin;
|