@envelop/response-cache 6.2.2 → 6.2.3

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 CHANGED
@@ -184,45 +184,34 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
184
184
  },
185
185
  };
186
186
  }
187
- function processResult(data) {
188
- if (data == null || typeof data !== 'object') {
189
- return;
190
- }
191
- if (Array.isArray(data)) {
192
- for (const item of data) {
193
- processResult(item);
194
- }
195
- return;
196
- }
197
- const typename = data.__responseCacheTypeName ?? data.__typename;
198
- delete data.__responseCacheTypeName;
199
- const idField = typename && idFieldByTypeName.get(typename);
200
- const entityId = data.__responseCacheId ?? (idField && data[idField]);
201
- delete data.__responseCacheId;
202
- // Always process nested objects, even if we are skipping cache, to ensure the result is cleaned up
203
- // of metadata fields added to the query document.
204
- for (const fieldName in data) {
205
- processResult(data[fieldName]);
206
- }
187
+ function onEntity(entity, data) {
207
188
  if (skip) {
208
189
  return;
209
190
  }
210
- if (ignoredTypesMap.has(typename) || (!sessionId && isPrivate(typename, data))) {
191
+ if (ignoredTypesMap.has(entity.typename) ||
192
+ (!sessionId && isPrivate(entity.typename, data))) {
211
193
  skip = true;
212
194
  return;
213
195
  }
214
- types.add(typename);
215
- if (typename in ttlPerType) {
216
- const maybeTtl = ttlPerType[typename];
196
+ // in case the entity has no id, we attempt to extract it from the data
197
+ if (!entity.id) {
198
+ const idField = idFieldByTypeName.get(entity.typename);
199
+ if (idField) {
200
+ entity.id = data[idField];
201
+ }
202
+ }
203
+ types.add(entity.typename);
204
+ if (entity.typename in ttlPerType) {
205
+ const maybeTtl = ttlPerType[entity.typename];
217
206
  currentTtl = calculateTtl(maybeTtl, currentTtl);
218
207
  }
219
- if (entityId != null) {
220
- identifier.set(`${typename}:${entityId}`, { typename, id: entityId });
208
+ if (entity.id != null) {
209
+ identifier.set(`${entity.typename}:${entity.id}`, entity);
221
210
  }
222
211
  for (const fieldName in data) {
223
212
  const fieldData = data[fieldName];
224
213
  if (fieldData == null || (Array.isArray(fieldData) && fieldData.length === 0)) {
225
- const inferredTypes = typePerSchemaCoordinateMap.get(`${typename}.${fieldName}`);
214
+ const inferredTypes = typePerSchemaCoordinateMap.get(`${entity.typename}.${fieldName}`);
226
215
  inferredTypes?.forEach(inferredType => {
227
216
  if (inferredType in ttlPerType) {
228
217
  const maybeTtl = ttlPerType[inferredType];
@@ -234,7 +223,10 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
234
223
  }
235
224
  }
236
225
  function invalidateCache(result, setResult) {
237
- processResult(result.data);
226
+ result = { ...result };
227
+ if (result.data) {
228
+ result.data = removeMetadataFieldsFromResult(result.data, onEntity);
229
+ }
238
230
  const cacheInstance = cacheFactory(onExecuteParams.args.contextValue);
239
231
  if (cacheInstance == null) {
240
232
  // eslint-disable-next-line no-console
@@ -286,7 +278,9 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
286
278
  });
287
279
  }
288
280
  function maybeCacheResult(result, setResult) {
289
- processResult(result.data);
281
+ if (result.data) {
282
+ result.data = removeMetadataFieldsFromResult(result.data, onEntity);
283
+ }
290
284
  // we only use the global ttl if no currentTtl has been determined.
291
285
  const finalTtl = currentTtl ?? globalTtl;
292
286
  if (skip || !shouldCacheResult({ cacheKey, result }) || finalTtl === 0) {
@@ -347,9 +341,28 @@ function handleAsyncIterableResult(handler) {
347
341
  }
348
342
  }
349
343
  const newResult = { ...payload.result };
344
+ // Handle initial/single result
350
345
  if (newResult.data) {
351
346
  newResult.data = removeMetadataFieldsFromResult(newResult.data);
352
347
  }
348
+ // Handle Incremental results
349
+ if ('hasNext' in newResult && newResult.incremental) {
350
+ newResult.incremental = newResult.incremental.map(value => {
351
+ if ('items' in value && value.items) {
352
+ return {
353
+ ...value,
354
+ items: removeMetadataFieldsFromResult(value.items),
355
+ };
356
+ }
357
+ if ('data' in value && value.data) {
358
+ return {
359
+ ...value,
360
+ data: removeMetadataFieldsFromResult(value.data),
361
+ };
362
+ }
363
+ return value;
364
+ });
365
+ }
353
366
  payload.setResult(newResult);
354
367
  },
355
368
  };
@@ -376,14 +389,17 @@ function calculateTtl(typeTtl, currentTtl) {
376
389
  }
377
390
  return currentTtl;
378
391
  }
379
- function unwrapTypenames(type) {
380
- if (type.ofType) {
381
- return unwrapTypenames(type.ofType);
392
+ function unwrapTypenames(ttype) {
393
+ if ((0, graphql_1.isListType)(ttype) || (0, graphql_1.isNonNullType)(ttype)) {
394
+ return unwrapTypenames(ttype.ofType);
382
395
  }
383
- if (type._types) {
384
- return type._types.map((t) => unwrapTypenames(t)).flat();
396
+ if ((0, graphql_1.isUnionType)(ttype)) {
397
+ return ttype
398
+ .getTypes()
399
+ .map(ttype => unwrapTypenames(ttype))
400
+ .flat();
385
401
  }
386
- return [type.name];
402
+ return [ttype.name];
387
403
  }
388
404
  exports.cacheControlDirective = `
389
405
  enum CacheControlScope {
@@ -393,17 +409,30 @@ exports.cacheControlDirective = `
393
409
 
394
410
  directive @cacheControl(maxAge: Int, scope: CacheControlScope) on FIELD_DEFINITION | OBJECT
395
411
  `;
396
- function removeMetadataFieldsFromResult(data) {
412
+ function removeMetadataFieldsFromResult(data, onEntity) {
397
413
  if (Array.isArray(data)) {
398
- return data.map(removeMetadataFieldsFromResult);
414
+ return data.map(record => removeMetadataFieldsFromResult(record, onEntity));
399
415
  }
400
416
  // clone the data to avoid mutation
401
417
  data = { ...data };
402
- delete data.__responseCacheTypeName;
403
- delete data.__responseCacheId;
418
+ const typename = data.__responseCacheTypeName ?? data.__typename;
419
+ if (typeof typename === 'string') {
420
+ const entity = { typename };
421
+ delete data.__responseCacheTypeName;
422
+ if (data.__responseCacheId &&
423
+ (typeof data.__responseCacheId === 'string' || typeof data.__responseCacheId === 'number')) {
424
+ entity.id = data.__responseCacheId;
425
+ delete data.__responseCacheId;
426
+ }
427
+ onEntity?.(entity, data);
428
+ }
404
429
  for (const key in data) {
405
- if (typeof data[key] === 'object') {
406
- data[key] = removeMetadataFieldsFromResult(data[key]);
430
+ const value = data[key];
431
+ if (Array.isArray(value)) {
432
+ data[key] = removeMetadataFieldsFromResult(value, onEntity);
433
+ }
434
+ if (value !== null && typeof value === 'object') {
435
+ data[key] = removeMetadataFieldsFromResult(value, onEntity);
407
436
  }
408
437
  }
409
438
  return data;
package/esm/plugin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import jsonStableStringify from 'fast-json-stable-stringify';
2
- import { getOperationAST, Kind, print, TypeInfo, visit, visitWithTypeInfo, } from 'graphql';
2
+ import { getOperationAST, isListType, isNonNullType, isUnionType, Kind, print, TypeInfo, visit, visitWithTypeInfo, } from 'graphql';
3
3
  import { getDocumentString, isAsyncIterable, } from '@envelop/core';
4
4
  import { getDirective, MapperKind, mapSchema, memoize1, memoize4, mergeIncrementalResult, } from '@graphql-tools/utils';
5
5
  import { hashSHA256 } from './hash-sha256.js';
@@ -177,45 +177,34 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
177
177
  },
178
178
  };
179
179
  }
180
- function processResult(data) {
181
- if (data == null || typeof data !== 'object') {
182
- return;
183
- }
184
- if (Array.isArray(data)) {
185
- for (const item of data) {
186
- processResult(item);
187
- }
188
- return;
189
- }
190
- const typename = data.__responseCacheTypeName ?? data.__typename;
191
- delete data.__responseCacheTypeName;
192
- const idField = typename && idFieldByTypeName.get(typename);
193
- const entityId = data.__responseCacheId ?? (idField && data[idField]);
194
- delete data.__responseCacheId;
195
- // Always process nested objects, even if we are skipping cache, to ensure the result is cleaned up
196
- // of metadata fields added to the query document.
197
- for (const fieldName in data) {
198
- processResult(data[fieldName]);
199
- }
180
+ function onEntity(entity, data) {
200
181
  if (skip) {
201
182
  return;
202
183
  }
203
- if (ignoredTypesMap.has(typename) || (!sessionId && isPrivate(typename, data))) {
184
+ if (ignoredTypesMap.has(entity.typename) ||
185
+ (!sessionId && isPrivate(entity.typename, data))) {
204
186
  skip = true;
205
187
  return;
206
188
  }
207
- types.add(typename);
208
- if (typename in ttlPerType) {
209
- const maybeTtl = ttlPerType[typename];
189
+ // in case the entity has no id, we attempt to extract it from the data
190
+ if (!entity.id) {
191
+ const idField = idFieldByTypeName.get(entity.typename);
192
+ if (idField) {
193
+ entity.id = data[idField];
194
+ }
195
+ }
196
+ types.add(entity.typename);
197
+ if (entity.typename in ttlPerType) {
198
+ const maybeTtl = ttlPerType[entity.typename];
210
199
  currentTtl = calculateTtl(maybeTtl, currentTtl);
211
200
  }
212
- if (entityId != null) {
213
- identifier.set(`${typename}:${entityId}`, { typename, id: entityId });
201
+ if (entity.id != null) {
202
+ identifier.set(`${entity.typename}:${entity.id}`, entity);
214
203
  }
215
204
  for (const fieldName in data) {
216
205
  const fieldData = data[fieldName];
217
206
  if (fieldData == null || (Array.isArray(fieldData) && fieldData.length === 0)) {
218
- const inferredTypes = typePerSchemaCoordinateMap.get(`${typename}.${fieldName}`);
207
+ const inferredTypes = typePerSchemaCoordinateMap.get(`${entity.typename}.${fieldName}`);
219
208
  inferredTypes?.forEach(inferredType => {
220
209
  if (inferredType in ttlPerType) {
221
210
  const maybeTtl = ttlPerType[inferredType];
@@ -227,7 +216,10 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
227
216
  }
228
217
  }
229
218
  function invalidateCache(result, setResult) {
230
- processResult(result.data);
219
+ result = { ...result };
220
+ if (result.data) {
221
+ result.data = removeMetadataFieldsFromResult(result.data, onEntity);
222
+ }
231
223
  const cacheInstance = cacheFactory(onExecuteParams.args.contextValue);
232
224
  if (cacheInstance == null) {
233
225
  // eslint-disable-next-line no-console
@@ -279,7 +271,9 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
279
271
  });
280
272
  }
281
273
  function maybeCacheResult(result, setResult) {
282
- processResult(result.data);
274
+ if (result.data) {
275
+ result.data = removeMetadataFieldsFromResult(result.data, onEntity);
276
+ }
283
277
  // we only use the global ttl if no currentTtl has been determined.
284
278
  const finalTtl = currentTtl ?? globalTtl;
285
279
  if (skip || !shouldCacheResult({ cacheKey, result }) || finalTtl === 0) {
@@ -339,9 +333,28 @@ function handleAsyncIterableResult(handler) {
339
333
  }
340
334
  }
341
335
  const newResult = { ...payload.result };
336
+ // Handle initial/single result
342
337
  if (newResult.data) {
343
338
  newResult.data = removeMetadataFieldsFromResult(newResult.data);
344
339
  }
340
+ // Handle Incremental results
341
+ if ('hasNext' in newResult && newResult.incremental) {
342
+ newResult.incremental = newResult.incremental.map(value => {
343
+ if ('items' in value && value.items) {
344
+ return {
345
+ ...value,
346
+ items: removeMetadataFieldsFromResult(value.items),
347
+ };
348
+ }
349
+ if ('data' in value && value.data) {
350
+ return {
351
+ ...value,
352
+ data: removeMetadataFieldsFromResult(value.data),
353
+ };
354
+ }
355
+ return value;
356
+ });
357
+ }
345
358
  payload.setResult(newResult);
346
359
  },
347
360
  };
@@ -367,14 +380,17 @@ function calculateTtl(typeTtl, currentTtl) {
367
380
  }
368
381
  return currentTtl;
369
382
  }
370
- function unwrapTypenames(type) {
371
- if (type.ofType) {
372
- return unwrapTypenames(type.ofType);
383
+ function unwrapTypenames(ttype) {
384
+ if (isListType(ttype) || isNonNullType(ttype)) {
385
+ return unwrapTypenames(ttype.ofType);
373
386
  }
374
- if (type._types) {
375
- return type._types.map((t) => unwrapTypenames(t)).flat();
387
+ if (isUnionType(ttype)) {
388
+ return ttype
389
+ .getTypes()
390
+ .map(ttype => unwrapTypenames(ttype))
391
+ .flat();
376
392
  }
377
- return [type.name];
393
+ return [ttype.name];
378
394
  }
379
395
  export const cacheControlDirective = /* GraphQL */ `
380
396
  enum CacheControlScope {
@@ -384,17 +400,30 @@ export const cacheControlDirective = /* GraphQL */ `
384
400
 
385
401
  directive @cacheControl(maxAge: Int, scope: CacheControlScope) on FIELD_DEFINITION | OBJECT
386
402
  `;
387
- function removeMetadataFieldsFromResult(data) {
403
+ function removeMetadataFieldsFromResult(data, onEntity) {
388
404
  if (Array.isArray(data)) {
389
- return data.map(removeMetadataFieldsFromResult);
405
+ return data.map(record => removeMetadataFieldsFromResult(record, onEntity));
390
406
  }
391
407
  // clone the data to avoid mutation
392
408
  data = { ...data };
393
- delete data.__responseCacheTypeName;
394
- delete data.__responseCacheId;
409
+ const typename = data.__responseCacheTypeName ?? data.__typename;
410
+ if (typeof typename === 'string') {
411
+ const entity = { typename };
412
+ delete data.__responseCacheTypeName;
413
+ if (data.__responseCacheId &&
414
+ (typeof data.__responseCacheId === 'string' || typeof data.__responseCacheId === 'number')) {
415
+ entity.id = data.__responseCacheId;
416
+ delete data.__responseCacheId;
417
+ }
418
+ onEntity?.(entity, data);
419
+ }
395
420
  for (const key in data) {
396
- if (typeof data[key] === 'object') {
397
- data[key] = removeMetadataFieldsFromResult(data[key]);
421
+ const value = data[key];
422
+ if (Array.isArray(value)) {
423
+ data[key] = removeMetadataFieldsFromResult(value, onEntity);
424
+ }
425
+ if (value !== null && typeof value === 'object') {
426
+ data[key] = removeMetadataFieldsFromResult(value, onEntity);
398
427
  }
399
428
  }
400
429
  return data;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envelop/response-cache",
3
- "version": "6.2.2",
3
+ "version": "6.2.3",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "@envelop/core": "^5.0.2",