@graphql-box/cache-manager 2.4.2 → 3.1.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.
Files changed (58) hide show
  1. package/lib/browser/index.js +1 -1
  2. package/lib/browser/index.js.map +1 -1
  3. package/lib/browser/production.analysis.txt +63 -27
  4. package/lib/main/helpers/isFirstResponseChunk.js +11 -0
  5. package/lib/main/helpers/isFirstResponseChunk.js.map +1 -0
  6. package/lib/main/helpers/isLastResponseChunk.js +11 -0
  7. package/lib/main/helpers/isLastResponseChunk.js.map +1 -0
  8. package/lib/main/helpers/isNotLastResponseChunk.js +11 -0
  9. package/lib/main/helpers/isNotLastResponseChunk.js.map +1 -0
  10. package/lib/main/helpers/isNotResponseChunk.js +11 -0
  11. package/lib/main/helpers/isNotResponseChunk.js.map +1 -0
  12. package/lib/main/helpers/mergeResponseDataSets.js +53 -0
  13. package/lib/main/helpers/mergeResponseDataSets.js.map +1 -0
  14. package/lib/main/helpers/normalizePatchResponseData.js +10 -5
  15. package/lib/main/helpers/normalizePatchResponseData.js.map +1 -1
  16. package/lib/main/main/index.js +145 -121
  17. package/lib/main/main/index.js.map +1 -1
  18. package/lib/module/helpers/isFirstResponseChunk.js +2 -0
  19. package/lib/module/helpers/isFirstResponseChunk.js.map +1 -0
  20. package/lib/module/helpers/isLastResponseChunk.js +2 -0
  21. package/lib/module/helpers/isLastResponseChunk.js.map +1 -0
  22. package/lib/module/helpers/isNotLastResponseChunk.js +2 -0
  23. package/lib/module/helpers/isNotLastResponseChunk.js.map +1 -0
  24. package/lib/module/helpers/isNotResponseChunk.js +2 -0
  25. package/lib/module/helpers/isNotResponseChunk.js.map +1 -0
  26. package/lib/module/helpers/mergeResponseDataSets.js +41 -0
  27. package/lib/module/helpers/mergeResponseDataSets.js.map +1 -0
  28. package/lib/module/helpers/normalizePatchResponseData.js +10 -5
  29. package/lib/module/helpers/normalizePatchResponseData.js.map +1 -1
  30. package/lib/module/main/index.js +142 -121
  31. package/lib/module/main/index.js.map +1 -1
  32. package/lib/types/defs/index.d.ts +3 -10
  33. package/lib/types/defs/index.d.ts.map +1 -1
  34. package/lib/types/helpers/isFirstResponseChunk.d.ts +5 -0
  35. package/lib/types/helpers/isFirstResponseChunk.d.ts.map +1 -0
  36. package/lib/types/helpers/isLastResponseChunk.d.ts +5 -0
  37. package/lib/types/helpers/isLastResponseChunk.d.ts.map +1 -0
  38. package/lib/types/helpers/isNotLastResponseChunk.d.ts +5 -0
  39. package/lib/types/helpers/isNotLastResponseChunk.d.ts.map +1 -0
  40. package/lib/types/helpers/isNotResponseChunk.d.ts +5 -0
  41. package/lib/types/helpers/isNotResponseChunk.d.ts.map +1 -0
  42. package/lib/types/helpers/mergeResponseDataSets.d.ts +4 -0
  43. package/lib/types/helpers/mergeResponseDataSets.d.ts.map +1 -0
  44. package/lib/types/helpers/normalizePatchResponseData.d.ts +2 -8
  45. package/lib/types/helpers/normalizePatchResponseData.d.ts.map +1 -1
  46. package/lib/types/main/index.d.ts +7 -5
  47. package/lib/types/main/index.d.ts.map +1 -1
  48. package/package.json +4 -4
  49. package/src/__snapshots__/index.test.ts.snap +12481 -11881
  50. package/src/defs/index.ts +14 -22
  51. package/src/helpers/isFirstResponseChunk.ts +5 -0
  52. package/src/helpers/isLastResponseChunk.ts +5 -0
  53. package/src/helpers/isNotLastResponseChunk.ts +5 -0
  54. package/src/helpers/isNotResponseChunk.ts +5 -0
  55. package/src/helpers/mergeResponseDataSets.ts +35 -0
  56. package/src/helpers/normalizePatchResponseData.ts +8 -1
  57. package/src/index.test.ts +191 -115
  58. package/src/main/index.ts +171 -137
package/src/main/index.ts CHANGED
@@ -49,7 +49,6 @@ import {
49
49
  FieldCount,
50
50
  FieldPathChecklist,
51
51
  FieldPathChecklistValue,
52
- InitOptions,
53
52
  KeysAndPaths,
54
53
  MergedCachedFieldData,
55
54
  PartialQueryResponse,
@@ -63,29 +62,14 @@ import { buildFieldKeysAndPaths } from "../helpers/buildKeysAndPaths";
63
62
  import deriveOpCacheability from "../helpers/deriveOpCacheability";
64
63
  import filterOutPropsWithArgsOrDirectives from "../helpers/filterOutPropsWithArgsOrDirectives";
65
64
  import filterQuery from "../helpers/filterQuery";
65
+ import isLastResponseChunk from "../helpers/isLastResponseChunk";
66
+ import isNotLastResponseChunk from "../helpers/isNotLastResponseChunk";
67
+ import isNotResponseChunk from "../helpers/isNotResponseChunk";
68
+ import mergeResponseDataSets from "../helpers/mergeResponseDataSets";
66
69
  import normalizePatchResponseData from "../helpers/normalizePatchResponseData";
67
70
  import { getValidTypeIDValue } from "../helpers/validTypeIDValue";
68
71
 
69
72
  export class CacheManager implements CacheManagerDef {
70
- public static async init(options: InitOptions): Promise<CacheManager> {
71
- const errors: TypeError[] = [];
72
-
73
- if (!options.cache) {
74
- errors.push(new TypeError("@graphql-box/cache-manager expected options.cache."));
75
- }
76
-
77
- if (!!options.typeCacheDirectives && !isPlainObject(options.typeCacheDirectives)) {
78
- const message = "@graphql-box/cache-manager expected options.typeCacheDirectives to be a plain object.";
79
- errors.push(new TypeError(message));
80
- }
81
-
82
- if (errors.length) {
83
- return Promise.reject(errors);
84
- }
85
-
86
- return new CacheManager(options);
87
- }
88
-
89
73
  private static _countFieldPathChecklist(fieldPathChecklist: FieldPathChecklist): FieldCount {
90
74
  const fieldCount: FieldCount = { missing: 0, total: 0 };
91
75
 
@@ -225,16 +209,31 @@ export class CacheManager implements CacheManagerDef {
225
209
  private _cascadeCacheControl: boolean;
226
210
  private _fallbackOperationCacheability: string;
227
211
  private _partialQueryResponses: PartialQueryResponses = new Map();
212
+ private _responseChunksAwaitingCaching: Map<string, RawResponseDataWithMaybeCacheMetadata[]> = new Map();
228
213
  private _typeCacheDirectives: PlainObjectStringMap;
229
214
  private _typeIDKey: string;
230
215
 
231
216
  constructor(options: ConstructorOptions) {
232
- const { cache, cascadeCacheControl, fallbackOperationCacheability, typeCacheDirectives, typeIDKey } = options;
233
- this._cache = cache;
234
- this._cascadeCacheControl = cascadeCacheControl || false;
235
- this._fallbackOperationCacheability = fallbackOperationCacheability || NO_CACHE;
236
- this._typeCacheDirectives = typeCacheDirectives || {};
237
- this._typeIDKey = typeIDKey;
217
+ const errors: TypeError[] = [];
218
+
219
+ if (!options.cache) {
220
+ errors.push(new TypeError("@graphql-box/cache-manager expected options.cache."));
221
+ }
222
+
223
+ if (!!options.typeCacheDirectives && !isPlainObject(options.typeCacheDirectives)) {
224
+ const message = "@graphql-box/cache-manager expected options.typeCacheDirectives to be a plain object.";
225
+ errors.push(new TypeError(message));
226
+ }
227
+
228
+ if (errors.length) {
229
+ throw errors;
230
+ }
231
+
232
+ this._cache = options.cache;
233
+ this._cascadeCacheControl = options.cascadeCacheControl || false;
234
+ this._fallbackOperationCacheability = options.fallbackOperationCacheability || NO_CACHE;
235
+ this._typeCacheDirectives = options.typeCacheDirectives || {};
236
+ this._typeIDKey = options.typeIDKey;
238
237
  }
239
238
 
240
239
  get cache(): Cachemap {
@@ -283,6 +282,37 @@ export class CacheManager implements CacheManagerDef {
283
282
  return { updated: { ast: filteredAST, hash: hashRequest(request), request } };
284
283
  }
285
284
 
285
+ public async cacheQuery(
286
+ requestData: RequestData,
287
+ updatedRequestData: RequestData,
288
+ rawResponseData: RawResponseDataWithMaybeCacheMetadata,
289
+ options: RequestOptions,
290
+ context: RequestContext,
291
+ ): Promise<ResponseData> {
292
+ const cacheManagerContext: CacheManagerContext = {
293
+ ...context,
294
+ fragmentDefinitions: getFragmentDefinitions(updatedRequestData.ast),
295
+ typeIDKey: this._typeIDKey,
296
+ };
297
+
298
+ return this._cacheResponse(requestData, updatedRequestData, rawResponseData, options, cacheManagerContext);
299
+ }
300
+
301
+ public async cacheResponse(
302
+ requestData: RequestData,
303
+ rawResponseData: RawResponseDataWithMaybeCacheMetadata,
304
+ options: RequestOptions,
305
+ context: RequestContext,
306
+ ): Promise<ResponseData> {
307
+ const cacheManagerContext: CacheManagerContext = {
308
+ ...context,
309
+ fragmentDefinitions: getFragmentDefinitions(requestData.ast),
310
+ typeIDKey: this._typeIDKey,
311
+ };
312
+
313
+ return this._cacheResponse(requestData, undefined, rawResponseData, options, cacheManagerContext);
314
+ }
315
+
286
316
  public async checkCacheEntry(
287
317
  cacheType: CacheTypes,
288
318
  hash: string,
@@ -315,83 +345,6 @@ export class CacheManager implements CacheManagerDef {
315
345
  this._partialQueryResponses.delete(hash);
316
346
  }
317
347
 
318
- public async resolveQuery(
319
- requestData: RequestData,
320
- updatedRequestData: RequestData,
321
- rawResponseData: RawResponseDataWithMaybeCacheMetadata,
322
- options: RequestOptions,
323
- context: RequestContext,
324
- ): Promise<ResponseData> {
325
- const cacheManagerContext: CacheManagerContext = {
326
- ...context,
327
- fragmentDefinitions: getFragmentDefinitions(updatedRequestData.ast),
328
- typeIDKey: this._typeIDKey,
329
- };
330
-
331
- const dataCaching: Promise<void>[] = [];
332
-
333
- const { cacheMetadata, data, hasNext, paths } = await this._resolveRequest(
334
- updatedRequestData,
335
- rawResponseData,
336
- options,
337
- cacheManagerContext,
338
- );
339
-
340
- let partialQueryResponse: PartialQueryResponse | undefined;
341
-
342
- if (cacheManagerContext.queryFiltered) {
343
- if (!(rawResponseData.hasNext || rawResponseData.paths)) {
344
- dataCaching.push(
345
- this._setQueryResponseCacheEntry(
346
- updatedRequestData.hash,
347
- { cacheMetadata, data },
348
- options,
349
- cacheManagerContext,
350
- ),
351
- );
352
- }
353
-
354
- if (!rawResponseData.paths) {
355
- partialQueryResponse = this._getPartialQueryResponse(requestData.hash);
356
- }
357
- }
358
-
359
- const responseCacheMetadata = CacheManager._mergeResponseCacheMetadata(cacheMetadata, partialQueryResponse);
360
- const responseData = this._mergeResponseData(data, partialQueryResponse);
361
-
362
- if (!(rawResponseData.hasNext || rawResponseData.paths)) {
363
- dataCaching.push(
364
- this._setQueryResponseCacheEntry(
365
- requestData.hash,
366
- { cacheMetadata: responseCacheMetadata, data: responseData },
367
- options,
368
- cacheManagerContext,
369
- ),
370
- );
371
- }
372
-
373
- if (options.awaitDataCaching) {
374
- await Promise.all(dataCaching);
375
- }
376
-
377
- return { cacheMetadata: responseCacheMetadata, data: responseData, hasNext, paths };
378
- }
379
-
380
- public async resolveRequest(
381
- requestData: RequestData,
382
- rawResponseData: RawResponseDataWithMaybeCacheMetadata,
383
- options: RequestOptions,
384
- context: RequestContext,
385
- ): Promise<ResponseData> {
386
- const cacheManagerContext: CacheManagerContext = {
387
- ...context,
388
- fragmentDefinitions: getFragmentDefinitions(requestData.ast),
389
- typeIDKey: this._typeIDKey,
390
- };
391
-
392
- return this._resolveRequest(requestData, rawResponseData, options, cacheManagerContext);
393
- }
394
-
395
348
  private async _analyzeFieldNode(
396
349
  fieldNode: FieldNode,
397
350
  cachedAncestorFieldData: CachedAncestorFieldData,
@@ -399,7 +352,7 @@ export class CacheManager implements CacheManagerDef {
399
352
  options: RequestOptions,
400
353
  context: CacheManagerContext,
401
354
  ): Promise<void> {
402
- if (hasChildFields(fieldNode)) {
355
+ if (hasChildFields(fieldNode, { fragmentDefinitions: context.fragmentDefinitions })) {
403
356
  await this._analyzeParentFieldNode(fieldNode, cachedAncestorFieldData, cachedResponseData, options, context);
404
357
  } else {
405
358
  await this._analyzeLeafFieldNode(fieldNode, cachedAncestorFieldData, cachedResponseData, options, context);
@@ -556,6 +509,93 @@ export class CacheManager implements CacheManagerDef {
556
509
  return cacheMetadata;
557
510
  }
558
511
 
512
+ private async _cacheResponse(
513
+ requestData: RequestData,
514
+ updatedRequestData: RequestData | undefined,
515
+ rawResponseData: RawResponseDataWithMaybeCacheMetadata,
516
+ options: RequestOptions,
517
+ context: CacheManagerContext,
518
+ ): Promise<ResponseData> {
519
+ const normalizedResponseData = normalizePatchResponseData(rawResponseData, context);
520
+ let responseDataForCaching: RawResponseDataWithMaybeCacheMetadata | undefined = normalizedResponseData;
521
+
522
+ if (isNotLastResponseChunk(rawResponseData, context)) {
523
+ this._setResponseChunksAwaitingCaching(normalizedResponseData, context);
524
+ responseDataForCaching = undefined;
525
+ }
526
+
527
+ if (isLastResponseChunk(rawResponseData, context)) {
528
+ responseDataForCaching = this._retrieveResponseDataForCaching(normalizedResponseData, context);
529
+ }
530
+
531
+ const dataCaching: Promise<void>[] = [];
532
+
533
+ if (responseDataForCaching) {
534
+ const { data } = responseDataForCaching;
535
+ const cacheMetadata = this._buildCacheMetadata(requestData, responseDataForCaching, options, context);
536
+
537
+ dataCaching.push(
538
+ this._setEntityAndRequestFieldPathCacheEntries(
539
+ requestData,
540
+ {
541
+ cacheMetadata,
542
+ entityData: cloneDeep(data),
543
+ requestFieldPathData: cloneDeep(data),
544
+ },
545
+ options,
546
+ context,
547
+ ),
548
+ );
549
+
550
+ let queryCacheMetadata: CacheMetadata | undefined;
551
+ let queryData: PlainObjectMap | undefined;
552
+
553
+ if (context.operation === QUERY) {
554
+ let partialQueryResponse: PartialQueryResponse | undefined;
555
+
556
+ if (context.queryFiltered && updatedRequestData) {
557
+ dataCaching.push(
558
+ this._setQueryResponseCacheEntry(updatedRequestData.hash, { cacheMetadata, data }, options, context),
559
+ );
560
+
561
+ partialQueryResponse = this._getPartialQueryResponse(requestData.hash);
562
+ }
563
+
564
+ queryCacheMetadata = CacheManager._mergeResponseCacheMetadata(cacheMetadata, partialQueryResponse);
565
+ queryData = this._mergeResponseData(data, partialQueryResponse);
566
+
567
+ dataCaching.push(
568
+ this._setQueryResponseCacheEntry(
569
+ requestData.hash,
570
+ { cacheMetadata: queryCacheMetadata, data: queryData },
571
+ options,
572
+ context,
573
+ ),
574
+ );
575
+ }
576
+
577
+ if (options.awaitDataCaching) {
578
+ await Promise.all(dataCaching);
579
+ }
580
+
581
+ if (isNotResponseChunk(normalizedResponseData, context) && queryCacheMetadata && queryData) {
582
+ return {
583
+ cacheMetadata: queryCacheMetadata,
584
+ data: queryData,
585
+ };
586
+ }
587
+ }
588
+
589
+ const { data, hasNext, paths } = normalizedResponseData;
590
+
591
+ return {
592
+ cacheMetadata: this._buildCacheMetadata(requestData, normalizedResponseData, options, context),
593
+ data,
594
+ hasNext,
595
+ paths,
596
+ };
597
+ }
598
+
559
599
  private async _checkCacheEntry(
560
600
  cacheType: CacheTypes,
561
601
  hash: string,
@@ -717,37 +757,6 @@ export class CacheManager implements CacheManagerDef {
717
757
  );
718
758
  }
719
759
 
720
- private async _resolveRequest(
721
- requestData: RequestData,
722
- rawResponseData: RawResponseDataWithMaybeCacheMetadata,
723
- options: RequestOptions,
724
- context: CacheManagerContext,
725
- ): Promise<ResponseData> {
726
- const normalizedResponseData =
727
- rawResponseData.paths && context.normalizePatchResponseData
728
- ? normalizePatchResponseData(rawResponseData)
729
- : rawResponseData;
730
-
731
- const dataCaching: Promise<void>[] = [];
732
- const cacheMetadata = this._buildCacheMetadata(requestData, normalizedResponseData, options, context);
733
- const { data, hasNext, paths } = normalizedResponseData;
734
-
735
- dataCaching.push(
736
- this._setEntityAndRequestFieldPathCacheEntries(
737
- requestData,
738
- { cacheMetadata, entityData: cloneDeep(data), requestFieldPathData: cloneDeep(data) },
739
- options,
740
- context,
741
- ),
742
- );
743
-
744
- if (options.awaitDataCaching) {
745
- await Promise.all(dataCaching);
746
- }
747
-
748
- return { cacheMetadata, data, hasNext, paths };
749
- }
750
-
751
760
  private async _retrieveCachedEntityData(
752
761
  validTypeIDValue: string | number,
753
762
  { possibleTypes, typeName }: FieldTypeInfo,
@@ -879,6 +888,18 @@ export class CacheManager implements CacheManagerDef {
879
888
  return cachedResponseData;
880
889
  }
881
890
 
891
+ private _retrieveResponseDataForCaching(
892
+ normalizedResponseData: RawResponseDataWithMaybeCacheMetadata,
893
+ context: CacheManagerContext,
894
+ ) {
895
+ const responseChunks = this._responseChunksAwaitingCaching.get(
896
+ context.boxID,
897
+ ) as RawResponseDataWithMaybeCacheMetadata[];
898
+
899
+ this._responseChunksAwaitingCaching.delete(context.boxID);
900
+ return mergeResponseDataSets([...responseChunks, normalizedResponseData]);
901
+ }
902
+
882
903
  @logCacheEntry()
883
904
  private async _setCacheEntry(
884
905
  cacheType: CacheTypes,
@@ -1119,7 +1140,7 @@ export class CacheManager implements CacheManagerDef {
1119
1140
  context,
1120
1141
  );
1121
1142
 
1122
- if (hasChildFields(field)) {
1143
+ if (hasChildFields(field, { fragmentDefinitions: context.fragmentDefinitions })) {
1123
1144
  if (isEntity) {
1124
1145
  set(data, responseDataPath, { __cacheKey: `${REQUEST_FIELD_PATHS}::${hashedRequestFieldCacheKey}` });
1125
1146
  } else {
@@ -1128,6 +1149,19 @@ export class CacheManager implements CacheManagerDef {
1128
1149
  }
1129
1150
  }
1130
1151
  }
1152
+
1153
+ private _setResponseChunksAwaitingCaching(
1154
+ normalizedResponseData: RawResponseDataWithMaybeCacheMetadata,
1155
+ context: CacheManagerContext,
1156
+ ) {
1157
+ const responseChunks = this._responseChunksAwaitingCaching.get(context.boxID);
1158
+
1159
+ if (responseChunks) {
1160
+ this._responseChunksAwaitingCaching.set(context.boxID, [...responseChunks, normalizedResponseData]);
1161
+ } else {
1162
+ this._responseChunksAwaitingCaching.set(context.boxID, [normalizedResponseData]);
1163
+ }
1164
+ }
1131
1165
  }
1132
1166
 
1133
1167
  export default function init(userOptions: UserOptions): CacheManagerInit {
@@ -1135,5 +1169,5 @@ export default function init(userOptions: UserOptions): CacheManagerInit {
1135
1169
  throw new TypeError("@graphql-box/cache-manager expected userOptions to be a plain object.");
1136
1170
  }
1137
1171
 
1138
- return (clientOptions: ClientOptions) => CacheManager.init({ ...clientOptions, ...userOptions });
1172
+ return (clientOptions: ClientOptions) => new CacheManager({ ...clientOptions, ...userOptions });
1139
1173
  }