@envelop/response-cache 5.1.0 → 5.2.0-alpha-20230625183613-3b72bf4a
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/plugin.js +95 -23
- package/esm/plugin.js +95 -23
- package/package.json +3 -2
package/cjs/plugin.js
CHANGED
|
@@ -233,32 +233,63 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
|
|
|
233
233
|
},
|
|
234
234
|
}));
|
|
235
235
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (
|
|
248
|
-
|
|
236
|
+
async function maybeCacheResult(result, setResult) {
|
|
237
|
+
const processedResult = processResult(result);
|
|
238
|
+
if (skip) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (!shouldCacheResult({ cacheKey, result: processedResult })) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// we only use the global ttl if no currentTtl has been determined.
|
|
245
|
+
const finalTtl = currentTtl ?? globalTtl;
|
|
246
|
+
if (finalTtl === 0) {
|
|
247
|
+
if (includeExtensionMetadata) {
|
|
248
|
+
setResult(resultWithMetadata(processedResult, { hit: false, didCache: false }));
|
|
249
249
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
cache.set(cacheKey, processedResult, identifier.values(), finalTtl);
|
|
253
|
+
if (includeExtensionMetadata) {
|
|
254
|
+
setResult(resultWithMetadata(processedResult, { hit: false, didCache: true, ttl: finalTtl }));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
onExecuteDone(payload) {
|
|
259
|
+
if (!(0, core_1.isAsyncIterable)(payload.result)) {
|
|
260
|
+
maybeCacheResult(payload.result, payload.setResult);
|
|
256
261
|
return;
|
|
257
262
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
263
|
+
// When the result is an AsyncIterable, it means the query is using @defer or @stream.
|
|
264
|
+
// This means we have to build the final result by merging the incremental results.
|
|
265
|
+
// The merged result is then used to know if we should cache it and to calculate the ttl.
|
|
266
|
+
let result = {};
|
|
267
|
+
return {
|
|
268
|
+
onNext(payload) {
|
|
269
|
+
const { data, errors, extensions, incremental, hasNext } = payload.result;
|
|
270
|
+
if (data) {
|
|
271
|
+
// This is the first result with the initial data payload sent to the client. We use it as the base result
|
|
272
|
+
if (data) {
|
|
273
|
+
result = { data };
|
|
274
|
+
}
|
|
275
|
+
if (errors) {
|
|
276
|
+
result.errors = errors;
|
|
277
|
+
}
|
|
278
|
+
if (extensions) {
|
|
279
|
+
result.extensions = extensions;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (incremental) {
|
|
283
|
+
for (const patch of incremental) {
|
|
284
|
+
mergePatch(result, patch);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (!hasNext) {
|
|
288
|
+
// The query is complete, we can process the final result
|
|
289
|
+
maybeCacheResult(result, payload.setResult);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
};
|
|
262
293
|
},
|
|
263
294
|
};
|
|
264
295
|
},
|
|
@@ -286,3 +317,44 @@ function calculateTtl(typeTtl, currentTtl) {
|
|
|
286
317
|
exports.cacheControlDirective = `
|
|
287
318
|
directive @cacheControl(maxAge: Int) on FIELD_DEFINITION | OBJECT
|
|
288
319
|
`;
|
|
320
|
+
function mergePatch(result, patch) {
|
|
321
|
+
// All errors and extensions are merged together in the final result
|
|
322
|
+
if (patch.errors) {
|
|
323
|
+
result.errors = [...(result.errors ?? []), ...patch.errors];
|
|
324
|
+
}
|
|
325
|
+
if (patch.extensions) {
|
|
326
|
+
result.extensions = { ...result.extensions, ...patch.extensions };
|
|
327
|
+
}
|
|
328
|
+
// We need to follow the path to the point where the patch should be applied
|
|
329
|
+
let target = result;
|
|
330
|
+
const path = ['data', ...(patch.path ?? [])];
|
|
331
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
332
|
+
target = target[path[i]];
|
|
333
|
+
}
|
|
334
|
+
const prop = path[path.length - 1];
|
|
335
|
+
const items = patch.items;
|
|
336
|
+
const data = patch.data;
|
|
337
|
+
if (items) {
|
|
338
|
+
// This is a stream patch, the last path segment should be a number and is the index at which we should begin to insert the new items
|
|
339
|
+
const start = +prop;
|
|
340
|
+
for (let i = 0; i < items.length; i++) {
|
|
341
|
+
target[start + i] = deepMerge(target[start + i], items[i]);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else if (data !== undefined) {
|
|
345
|
+
// This is a defer patch.
|
|
346
|
+
target[prop] = deepMerge(target[prop], data);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function deepMerge(target, source) {
|
|
350
|
+
if (typeof target === 'object' && target != null) {
|
|
351
|
+
target = Array.isArray(target) ? [...target] : { ...target };
|
|
352
|
+
for (const key of Object.keys(source)) {
|
|
353
|
+
target[key] = deepMerge(target[key], source[key]);
|
|
354
|
+
}
|
|
355
|
+
return target;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
return source;
|
|
359
|
+
}
|
|
360
|
+
}
|
package/esm/plugin.js
CHANGED
|
@@ -226,32 +226,63 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
|
|
|
226
226
|
},
|
|
227
227
|
}));
|
|
228
228
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (
|
|
241
|
-
|
|
229
|
+
async function maybeCacheResult(result, setResult) {
|
|
230
|
+
const processedResult = processResult(result);
|
|
231
|
+
if (skip) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!shouldCacheResult({ cacheKey, result: processedResult })) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// we only use the global ttl if no currentTtl has been determined.
|
|
238
|
+
const finalTtl = currentTtl ?? globalTtl;
|
|
239
|
+
if (finalTtl === 0) {
|
|
240
|
+
if (includeExtensionMetadata) {
|
|
241
|
+
setResult(resultWithMetadata(processedResult, { hit: false, didCache: false }));
|
|
242
242
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
cache.set(cacheKey, processedResult, identifier.values(), finalTtl);
|
|
246
|
+
if (includeExtensionMetadata) {
|
|
247
|
+
setResult(resultWithMetadata(processedResult, { hit: false, didCache: true, ttl: finalTtl }));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
onExecuteDone(payload) {
|
|
252
|
+
if (!isAsyncIterable(payload.result)) {
|
|
253
|
+
maybeCacheResult(payload.result, payload.setResult);
|
|
249
254
|
return;
|
|
250
255
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
256
|
+
// When the result is an AsyncIterable, it means the query is using @defer or @stream.
|
|
257
|
+
// This means we have to build the final result by merging the incremental results.
|
|
258
|
+
// The merged result is then used to know if we should cache it and to calculate the ttl.
|
|
259
|
+
let result = {};
|
|
260
|
+
return {
|
|
261
|
+
onNext(payload) {
|
|
262
|
+
const { data, errors, extensions, incremental, hasNext } = payload.result;
|
|
263
|
+
if (data) {
|
|
264
|
+
// This is the first result with the initial data payload sent to the client. We use it as the base result
|
|
265
|
+
if (data) {
|
|
266
|
+
result = { data };
|
|
267
|
+
}
|
|
268
|
+
if (errors) {
|
|
269
|
+
result.errors = errors;
|
|
270
|
+
}
|
|
271
|
+
if (extensions) {
|
|
272
|
+
result.extensions = extensions;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (incremental) {
|
|
276
|
+
for (const patch of incremental) {
|
|
277
|
+
mergePatch(result, patch);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (!hasNext) {
|
|
281
|
+
// The query is complete, we can process the final result
|
|
282
|
+
maybeCacheResult(result, payload.setResult);
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
};
|
|
255
286
|
},
|
|
256
287
|
};
|
|
257
288
|
},
|
|
@@ -278,3 +309,44 @@ function calculateTtl(typeTtl, currentTtl) {
|
|
|
278
309
|
export const cacheControlDirective = /* GraphQL */ `
|
|
279
310
|
directive @cacheControl(maxAge: Int) on FIELD_DEFINITION | OBJECT
|
|
280
311
|
`;
|
|
312
|
+
function mergePatch(result, patch) {
|
|
313
|
+
// All errors and extensions are merged together in the final result
|
|
314
|
+
if (patch.errors) {
|
|
315
|
+
result.errors = [...(result.errors ?? []), ...patch.errors];
|
|
316
|
+
}
|
|
317
|
+
if (patch.extensions) {
|
|
318
|
+
result.extensions = { ...result.extensions, ...patch.extensions };
|
|
319
|
+
}
|
|
320
|
+
// We need to follow the path to the point where the patch should be applied
|
|
321
|
+
let target = result;
|
|
322
|
+
const path = ['data', ...(patch.path ?? [])];
|
|
323
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
324
|
+
target = target[path[i]];
|
|
325
|
+
}
|
|
326
|
+
const prop = path[path.length - 1];
|
|
327
|
+
const items = patch.items;
|
|
328
|
+
const data = patch.data;
|
|
329
|
+
if (items) {
|
|
330
|
+
// This is a stream patch, the last path segment should be a number and is the index at which we should begin to insert the new items
|
|
331
|
+
const start = +prop;
|
|
332
|
+
for (let i = 0; i < items.length; i++) {
|
|
333
|
+
target[start + i] = deepMerge(target[start + i], items[i]);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
else if (data !== undefined) {
|
|
337
|
+
// This is a defer patch.
|
|
338
|
+
target[prop] = deepMerge(target[prop], data);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function deepMerge(target, source) {
|
|
342
|
+
if (typeof target === 'object' && target != null) {
|
|
343
|
+
target = Array.isArray(target) ? [...target] : { ...target };
|
|
344
|
+
for (const key of Object.keys(source)) {
|
|
345
|
+
target[key] = deepMerge(target[key], source[key]);
|
|
346
|
+
}
|
|
347
|
+
return target;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
return source;
|
|
351
|
+
}
|
|
352
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/response-cache",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0-alpha-20230625183613-3b72bf4a",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@envelop/core": "
|
|
6
|
+
"@envelop/core": "4.0.1-alpha-20230625183613-3b72bf4a",
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
+
"@graphql-tools/executor": "^1.1.0",
|
|
10
11
|
"@graphql-tools/utils": "^10.0.0",
|
|
11
12
|
"@whatwg-node/fetch": "^0.9.0",
|
|
12
13
|
"fast-json-stable-stringify": "^2.1.0",
|