@graphql-box/cache-manager 5.1.0 → 5.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.
Files changed (49) hide show
  1. package/dist/cjs/index.cjs +1 -1
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/esm/index.mjs +1 -1
  4. package/dist/esm/index.mjs.map +1 -1
  5. package/dist/production.analysis.txt +18 -47
  6. package/dist/types/cjs/helpers/filterOutPropsWithArgsOrDirectives.d.cts +6 -0
  7. package/dist/types/cjs/helpers/filterOutPropsWithArgsOrDirectives.d.cts.map +1 -0
  8. package/dist/types/cjs/helpers/validTypeIdValue.d.cts +1 -1
  9. package/dist/types/cjs/helpers/validTypeIdValue.d.cts.map +1 -1
  10. package/dist/types/cjs/main.d.cts +5 -3
  11. package/dist/types/cjs/main.d.cts.map +1 -1
  12. package/dist/types/cjs/types.d.cts +5 -5
  13. package/dist/types/cjs/types.d.cts.map +1 -1
  14. package/dist/types/esm/helpers/filterOutPropsWithArgsOrDirectives.d.ts +6 -0
  15. package/dist/types/esm/helpers/filterOutPropsWithArgsOrDirectives.d.ts.map +1 -0
  16. package/dist/types/esm/helpers/validTypeIdValue.d.ts +1 -1
  17. package/dist/types/esm/helpers/validTypeIdValue.d.ts.map +1 -1
  18. package/dist/types/esm/main.d.ts +5 -3
  19. package/dist/types/esm/main.d.ts.map +1 -1
  20. package/dist/types/esm/types.d.ts +5 -5
  21. package/dist/types/esm/types.d.ts.map +1 -1
  22. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  23. package/package.json +5 -5
  24. package/src/__snapshots__/index.test.ts.snap +18519 -18519
  25. package/src/helpers/filterOutPropsWithArgsOrDirectives.ts +29 -0
  26. package/src/helpers/validTypeIdValue.ts +3 -3
  27. package/src/index.test.ts +127 -153
  28. package/src/main.ts +179 -122
  29. package/src/types.ts +5 -5
  30. package/dist/types/cjs/helpers/createEntityDataKey.d.cts +0 -4
  31. package/dist/types/cjs/helpers/createEntityDataKey.d.cts.map +0 -1
  32. package/dist/types/cjs/helpers/filterOutPropsWithEntityArgsOrDirectives.d.cts +0 -5
  33. package/dist/types/cjs/helpers/filterOutPropsWithEntityArgsOrDirectives.d.cts.map +0 -1
  34. package/dist/types/cjs/helpers/filterOutPropsWithEntityOrArgs.d.cts +0 -6
  35. package/dist/types/cjs/helpers/filterOutPropsWithEntityOrArgs.d.cts.map +0 -1
  36. package/dist/types/cjs/helpers/isFieldEntity.d.cts +0 -3
  37. package/dist/types/cjs/helpers/isFieldEntity.d.cts.map +0 -1
  38. package/dist/types/esm/helpers/createEntityDataKey.d.ts +0 -4
  39. package/dist/types/esm/helpers/createEntityDataKey.d.ts.map +0 -1
  40. package/dist/types/esm/helpers/filterOutPropsWithEntityArgsOrDirectives.d.ts +0 -5
  41. package/dist/types/esm/helpers/filterOutPropsWithEntityArgsOrDirectives.d.ts.map +0 -1
  42. package/dist/types/esm/helpers/filterOutPropsWithEntityOrArgs.d.ts +0 -6
  43. package/dist/types/esm/helpers/filterOutPropsWithEntityOrArgs.d.ts.map +0 -1
  44. package/dist/types/esm/helpers/isFieldEntity.d.ts +0 -3
  45. package/dist/types/esm/helpers/isFieldEntity.d.ts.map +0 -1
  46. package/src/helpers/createEntityDataKey.ts +0 -11
  47. package/src/helpers/filterOutPropsWithEntityArgsOrDirectives.ts +0 -45
  48. package/src/helpers/filterOutPropsWithEntityOrArgs.ts +0 -31
  49. package/src/helpers/isFieldEntity.ts +0 -24
package/src/main.ts CHANGED
@@ -36,19 +36,16 @@ import {
36
36
  } from '@graphql-box/helpers';
37
37
  import { Cacheability } from 'cacheability';
38
38
  import { type FieldNode, Kind, OperationTypeNode, print } from 'graphql';
39
- import { assign, get, isEqual, isNumber, isUndefined, set, unset } from 'lodash-es';
39
+ import { assign, cloneDeep, get, isEqual, isNumber, isUndefined, set, unset } from 'lodash-es';
40
40
  import { CACHE_CONTROL, HEADER_NO_CACHE, METADATA, NO_CACHE } from './constants.ts';
41
41
  import { logCacheEntry, logCacheQuery, logPartialCompiled } from './debug/index.ts';
42
42
  import { areOnlyPopulatedFieldsTypeIdKeys } from './helpers/areOnlyPopulatedFieldsTypeIdKeys.ts';
43
43
  import { combineDataSets } from './helpers/combineData.ts';
44
- import { createEntityDataKey } from './helpers/createEntityDataKey.ts';
45
44
  import { deriveOpCacheability } from './helpers/deriveOpCacheability.ts';
46
- import { filterOutPropsWithEntityArgsOrDirectives } from './helpers/filterOutPropsWithEntityArgsOrDirectives.ts';
47
- import { filterOutPropsWithEntityOrArgs } from './helpers/filterOutPropsWithEntityOrArgs.ts';
45
+ import { filterOutPropsWithArgsOrDirectives } from './helpers/filterOutPropsWithArgsOrDirectives.ts';
48
46
  import { filterQuery } from './helpers/filterQuery.ts';
49
47
  import { getDataValue } from './helpers/getDataValue.ts';
50
48
  import { hasTypename } from './helpers/hasTypename.ts';
51
- import { isFieldEntity } from './helpers/isFieldEntity.ts';
52
49
  import { isLastResponseChunk } from './helpers/isLastResponseChunk.ts';
53
50
  import { isNotLastResponseChunk } from './helpers/isNotLastResponseChunk.ts';
54
51
  import { isNotResponseChunk } from './helpers/isNotResponseChunk.ts';
@@ -91,7 +88,7 @@ export class CacheManager implements CacheManagerDef {
91
88
 
92
89
  private static _getFieldDataFromAncestor<T>(ancestorFieldData: unknown, propNameOrIndex: string | number) {
93
90
  const dataValue = getDataValue<T>(ancestorFieldData, propNameOrIndex);
94
- return isObjectLike(dataValue) ? structuredClone(dataValue) : dataValue;
91
+ return isObjectLike(dataValue) ? cloneDeep(dataValue) : dataValue;
95
92
  }
96
93
 
97
94
  private static _getOperationCacheControl(cacheMetadata: CacheMetadata | undefined, operation: string): string {
@@ -279,7 +276,12 @@ export class CacheManager implements CacheManagerDef {
279
276
  }
280
277
 
281
278
  if (!fieldCount.missing) {
282
- this._setQueryResponseCacheEntry(hash, { cacheMetadata, data }, options, cacheManagerContext);
279
+ const dataCaching = this._setQueryResponseCacheEntry(hash, { cacheMetadata, data }, options, cacheManagerContext);
280
+
281
+ if (options.awaitDataCaching) {
282
+ await dataCaching;
283
+ }
284
+
283
285
  return { response: { cacheMetadata, data } };
284
286
  }
285
287
 
@@ -298,13 +300,13 @@ export class CacheManager implements CacheManagerDef {
298
300
  return this._cache;
299
301
  }
300
302
 
301
- public cacheQuery(
303
+ public async cacheQuery(
302
304
  requestData: RequestData,
303
305
  updatedRequestData: RequestData | undefined,
304
306
  rawResponseData: RawResponseDataWithMaybeCacheMetadata,
305
307
  options: RequestOptions,
306
308
  context: RequestContext
307
- ): ResponseData {
309
+ ): Promise<ResponseData> {
308
310
  const cacheManagerContext: CacheManagerContext = {
309
311
  ...context,
310
312
  fragmentDefinitions: getFragmentDefinitions((updatedRequestData ?? requestData).ast),
@@ -314,12 +316,12 @@ export class CacheManager implements CacheManagerDef {
314
316
  return this._cacheResponse(requestData, updatedRequestData, rawResponseData, options, cacheManagerContext);
315
317
  }
316
318
 
317
- public cacheResponse(
319
+ public async cacheResponse(
318
320
  requestData: RequestData,
319
321
  rawResponseData: RawResponseDataWithMaybeCacheMetadata,
320
322
  options: RequestOptions,
321
323
  context: RequestContext
322
- ): ResponseData {
324
+ ): Promise<ResponseData> {
323
325
  const cacheManagerContext: CacheManagerContext = {
324
326
  ...context,
325
327
  fragmentDefinitions: getFragmentDefinitions(requestData.ast),
@@ -361,13 +363,13 @@ export class CacheManager implements CacheManagerDef {
361
363
  this._partialQueryResponses.delete(hash);
362
364
  }
363
365
 
364
- public setQueryResponseCacheEntry(
366
+ public async setQueryResponseCacheEntry(
365
367
  requestData: RequestData,
366
368
  responseData: ResponseData,
367
369
  options: RequestOptions,
368
370
  context: CacheManagerContext
369
- ): void {
370
- this._setQueryResponseCacheEntry(requestData.hash, responseData, options, context);
371
+ ): Promise<void> {
372
+ return this._setQueryResponseCacheEntry(requestData.hash, responseData, options, context);
371
373
  }
372
374
 
373
375
  private async _analyzeFieldNode(
@@ -448,7 +450,7 @@ export class CacheManager implements CacheManagerDef {
448
450
  ): Promise<void> {
449
451
  const keysAndPaths = buildFieldKeysAndPaths(fieldNode, cachedAncestorFieldData, context);
450
452
  const { propNameOrIndex, requestFieldCacheKey, requestFieldPath } = keysAndPaths;
451
- const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
453
+ const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath)!;
452
454
 
453
455
  const { cacheability, data, entityData, requestFieldPathData } = await this._retrieveCachedParentNodeData(
454
456
  cachedAncestorFieldData,
@@ -551,13 +553,13 @@ export class CacheManager implements CacheManagerDef {
551
553
  return cacheMetadata;
552
554
  }
553
555
 
554
- private _cacheResponse(
556
+ private async _cacheResponse(
555
557
  requestData: RequestData,
556
558
  updatedRequestData: RequestData | undefined,
557
559
  rawResponseData: RawResponseDataWithMaybeCacheMetadata,
558
560
  options: RequestOptions,
559
561
  context: CacheManagerContext
560
- ): ResponseData {
562
+ ): Promise<ResponseData> {
561
563
  const normalizedResponseData = normalizePatchResponseData(rawResponseData, context);
562
564
  let responseDataForCaching: RawResponseDataWithMaybeCacheMetadata | undefined = normalizedResponseData;
563
565
 
@@ -570,22 +572,24 @@ export class CacheManager implements CacheManagerDef {
570
572
  responseDataForCaching = this._retrieveResponseDataForCaching(normalizedResponseData, context);
571
573
  }
572
574
 
575
+ const dataCaching: Promise<void>[] = [];
576
+
573
577
  if (responseDataForCaching) {
574
578
  const { data } = responseDataForCaching;
575
579
  const cacheMetadata = this._buildCacheMetadata(requestData, responseDataForCaching, options, context);
576
580
 
577
- void new Promise(() => {
581
+ dataCaching.push(
578
582
  this._setEntityAndRequestFieldPathCacheEntries(
579
583
  requestData,
580
584
  {
581
585
  cacheMetadata,
582
- entityData: structuredClone(data),
583
- requestFieldPathData: structuredClone(data),
586
+ entityData: cloneDeep(data),
587
+ requestFieldPathData: cloneDeep(data),
584
588
  },
585
589
  options,
586
590
  context
587
- );
588
- });
591
+ )
592
+ );
589
593
 
590
594
  let queryCacheMetadata: CacheMetadata | undefined;
591
595
  let queryData: PlainData | undefined;
@@ -594,21 +598,30 @@ export class CacheManager implements CacheManagerDef {
594
598
  let partialQueryResponse: PartialQueryResponse | undefined;
595
599
 
596
600
  if (context.queryFiltered && updatedRequestData) {
597
- this._setQueryResponseCacheEntry(updatedRequestData.hash, { cacheMetadata, data }, options, context);
601
+ dataCaching.push(
602
+ this._setQueryResponseCacheEntry(updatedRequestData.hash, { cacheMetadata, data }, options, context)
603
+ );
604
+
598
605
  partialQueryResponse = this._getPartialQueryResponse(requestData.hash);
599
606
  }
600
607
 
601
608
  queryCacheMetadata = CacheManager._mergeResponseCacheMetadata(cacheMetadata, partialQueryResponse);
602
609
  queryData = this._mergeResponseData(data, partialQueryResponse);
603
610
 
604
- this._setQueryResponseCacheEntry(
605
- requestData.hash,
606
- { cacheMetadata: queryCacheMetadata, data: queryData },
607
- options,
608
- context
611
+ dataCaching.push(
612
+ this._setQueryResponseCacheEntry(
613
+ requestData.hash,
614
+ { cacheMetadata: queryCacheMetadata, data: queryData },
615
+ options,
616
+ context
617
+ )
609
618
  );
610
619
  }
611
620
 
621
+ if (options.awaitDataCaching) {
622
+ await Promise.all(dataCaching);
623
+ }
624
+
612
625
  if (isNotResponseChunk(normalizedResponseData, context) && queryCacheMetadata && queryData) {
613
626
  return {
614
627
  cacheMetadata: queryCacheMetadata,
@@ -697,6 +710,22 @@ export class CacheManager implements CacheManagerDef {
697
710
  }
698
711
  }
699
712
 
713
+ private _isFieldEntity(fieldData: unknown, { isEntity, possibleTypes }: FieldTypeInfo): boolean {
714
+ if (!isPlainObject(fieldData) || !(this._typeIDKey in fieldData)) {
715
+ return false;
716
+ }
717
+
718
+ if (isEntity) {
719
+ return true;
720
+ }
721
+
722
+ if (possibleTypes.length === 0) {
723
+ return false;
724
+ }
725
+
726
+ return possibleTypes.some(type => type.typeName === fieldData.__typename);
727
+ }
728
+
700
729
  private _mergeResponseData(responseData: PlainData, partialQueryResponse?: PartialQueryResponse): PlainData {
701
730
  if (!partialQueryResponse) {
702
731
  return responseData;
@@ -705,24 +734,25 @@ export class CacheManager implements CacheManagerDef {
705
734
  return mergeDataSets(partialQueryResponse.data, responseData, this._typeIDKey);
706
735
  }
707
736
 
708
- private _parseEntityAndRequestFieldPathCacheEntryData(
737
+ private async _parseEntityAndRequestFieldPathCacheEntryData(
709
738
  field: FieldNode,
710
739
  ancestorKeysAndPaths: AncestorKeysAndPaths,
711
740
  { cacheMetadata, entityData, requestFieldPathData }: ResponseDataForCaching,
712
741
  options: RequestOptions,
713
742
  context: CacheManagerContext
714
- ): void {
743
+ ): Promise<void> {
715
744
  const keysAndPaths = buildFieldKeysAndPaths(field, ancestorKeysAndPaths, context);
716
- const { hashedRequestFieldCacheKey, requestFieldCacheKey, requestFieldPath, responseDataPath } = keysAndPaths;
745
+ const { requestFieldCacheKey, requestFieldPath, responseDataPath } = keysAndPaths;
717
746
  const fieldData = get(requestFieldPathData, responseDataPath) as unknown;
718
747
  const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
719
- const cacheability = cacheMetadata.get(requestFieldPath);
720
748
 
721
749
  if (!isObjectLike(fieldData) && !fieldTypeInfo?.hasDirectives) {
722
750
  return;
723
751
  }
724
752
 
725
753
  if (isObjectLike(fieldData)) {
754
+ const promises: Promise<void>[] = [];
755
+
726
756
  iterateChildFields(
727
757
  field,
728
758
  fieldData,
@@ -734,81 +764,41 @@ export class CacheManager implements CacheManagerDef {
734
764
  _fragmentName: string | undefined,
735
765
  childIndex?: number
736
766
  ) => {
737
- this._parseEntityAndRequestFieldPathCacheEntryData(
738
- childField,
739
- { index: childIndex, requestFieldCacheKey, requestFieldPath, responseDataPath },
740
- { cacheMetadata, entityData, requestFieldPathData },
741
- options,
742
- context
767
+ promises.push(
768
+ this._parseEntityAndRequestFieldPathCacheEntryData(
769
+ childField,
770
+ { index: childIndex, requestFieldCacheKey, requestFieldPath, responseDataPath },
771
+ { cacheMetadata, entityData, requestFieldPathData },
772
+ options,
773
+ context
774
+ )
743
775
  );
744
776
  }
745
777
  );
746
- }
747
-
748
- if (isUndefined(fieldData) || !fieldTypeInfo || !cacheability) {
749
- return;
750
- }
751
-
752
- const isEntity = isFieldEntity(fieldData, fieldTypeInfo, this._typeIDKey);
753
- const hasArgsOrDirectives = !!fieldTypeInfo.hasArguments || !!fieldTypeInfo.hasDirectives;
754
778
 
755
- if (context.operation === OperationTypeNode.QUERY && (isEntity || hasArgsOrDirectives)) {
756
- void this._setRequestFieldPathCacheEntry(
757
- keysAndPaths,
758
- {
759
- cacheability,
760
- fieldData: filterOutPropsWithEntityArgsOrDirectives(structuredClone(fieldData), field, keysAndPaths, context),
761
- fieldTypeInfo,
762
- },
763
- options,
764
- context
765
- );
766
-
767
- if (hasChildFields(field, { fragmentDefinitions: context.fragmentDefinitions })) {
768
- if (isEntity) {
769
- set(requestFieldPathData, responseDataPath, {
770
- __cacheKey: `${REQUEST_FIELD_PATHS}::${hashedRequestFieldCacheKey}`,
771
- });
772
- } else {
773
- unset(requestFieldPathData, responseDataPath);
774
- }
775
- }
779
+ await Promise.all(promises);
776
780
  }
777
781
 
778
- if (isEntity) {
779
- void this._setEntityCacheEntry(
780
- {
781
- cacheability,
782
- fieldData: filterOutPropsWithEntityOrArgs(
783
- structuredClone(get(entityData, responseDataPath)) as EntityData,
784
- field,
785
- keysAndPaths,
786
- context
787
- ),
788
- fieldTypeInfo,
789
- },
790
- options,
791
- context
792
- );
793
-
794
- set(entityData, responseDataPath, {
795
- __cacheKey: `${DATA_ENTITIES}::${createEntityDataKey(fieldData, fieldTypeInfo, context)}`,
796
- });
797
- }
782
+ await this._setEntityAndRequestFieldPathCacheEntry(
783
+ field,
784
+ keysAndPaths,
785
+ { cacheMetadata, entityData, requestFieldPathData },
786
+ options,
787
+ context
788
+ );
798
789
  }
799
790
 
800
791
  private async _retrieveCachedEntityData(
801
792
  validTypeIDValue: string | number,
802
- fieldTypeInfo: FieldTypeInfo | undefined,
793
+ { possibleTypes, typeName }: FieldTypeInfo,
803
794
  options: RequestOptions,
804
795
  context: CacheManagerContext
805
796
  ): Promise<Partial<CheckCacheEntryResult<EntityData>>> {
806
- const { possibleTypes = [], typeName } = fieldTypeInfo ?? {};
807
797
  const typeNames = [...possibleTypes.map(type => type.typeName), typeName];
808
798
 
809
799
  const checkResults = await Promise.all(
810
800
  typeNames.map(name =>
811
- this._checkCacheEntry<EntityData>(DATA_ENTITIES, `${String(name)}::${validTypeIDValue}`, options, context)
801
+ this._checkCacheEntry<EntityData>(DATA_ENTITIES, `${name}::${validTypeIDValue}`, options, context)
812
802
  )
813
803
  );
814
804
 
@@ -835,7 +825,7 @@ export class CacheManager implements CacheManagerDef {
835
825
  private async _retrieveCachedParentNodeData(
836
826
  { entityData: ancestorEntityData, requestFieldPathData: ancestorRequestFieldPathData }: CachedAncestorFieldData,
837
827
  { hashedRequestFieldCacheKey, propNameOrIndex, requestFieldCacheKey }: KeysAndPaths,
838
- fieldTypeInfo: FieldTypeInfo | undefined,
828
+ fieldTypeInfo: FieldTypeInfo,
839
829
  options: RequestOptions,
840
830
  context: CacheManagerContext
841
831
  ) {
@@ -952,18 +942,18 @@ export class CacheManager implements CacheManagerDef {
952
942
  _context: CacheManagerContext & { requestFieldCacheKey?: string }
953
943
  ): Promise<void> {
954
944
  try {
955
- await this._cache.set(`${cacheType}::${hash}`, value, cachemapOptions);
945
+ await this._cache.set(`${cacheType}::${hash}`, cloneDeep(value), cachemapOptions);
956
946
  } catch {
957
947
  // no catch
958
948
  }
959
949
  }
960
950
 
961
- private _setEntityAndRequestFieldPathCacheEntries(
951
+ private async _setEntityAndRequestFieldPathCacheEntries(
962
952
  requestData: RequestData,
963
953
  responseData: ResponseDataForCaching,
964
954
  options: RequestOptions,
965
955
  context: CacheManagerContext
966
- ): void {
956
+ ): Promise<void> {
967
957
  const operationNode = getOperationDefinitions(requestData.ast, context.operation)[0];
968
958
 
969
959
  if (!operationNode) {
@@ -976,22 +966,69 @@ export class CacheManager implements CacheManagerDef {
976
966
  return;
977
967
  }
978
968
 
979
- fieldsAndTypeNames.map(({ fieldNode }) => {
980
- this._parseEntityAndRequestFieldPathCacheEntryData(
981
- fieldNode,
982
- { requestFieldPath: context.operation },
983
- responseData,
969
+ await Promise.all(
970
+ fieldsAndTypeNames.map(({ fieldNode }) => {
971
+ return this._parseEntityAndRequestFieldPathCacheEntryData(
972
+ fieldNode,
973
+ { requestFieldPath: context.operation },
974
+ responseData,
975
+ options,
976
+ context
977
+ );
978
+ })
979
+ );
980
+ }
981
+
982
+ private async _setEntityAndRequestFieldPathCacheEntry(
983
+ field: FieldNode,
984
+ keysAndPaths: KeysAndPaths,
985
+ { cacheMetadata, entityData, requestFieldPathData }: ResponseDataForCaching,
986
+ options: RequestOptions,
987
+ context: CacheManagerContext
988
+ ) {
989
+ const { requestFieldPath, responseDataPath } = keysAndPaths;
990
+ const fieldData = get(entityData, responseDataPath) as unknown;
991
+ const fieldTypeInfo = context.fieldTypeMap.get(requestFieldPath);
992
+ const cacheability = cacheMetadata.get(requestFieldPath);
993
+
994
+ if (isUndefined(fieldData) || !fieldTypeInfo || !cacheability) {
995
+ return;
996
+ }
997
+
998
+ const promises: Promise<void>[] = [];
999
+
1000
+ promises.push(
1001
+ this._setRequestFieldPathCacheEntry(
1002
+ field,
1003
+ keysAndPaths,
1004
+ { cacheability, data: requestFieldPathData, fieldTypeInfo },
984
1005
  options,
985
1006
  context
1007
+ )
1008
+ );
1009
+
1010
+ const isEntity = this._isFieldEntity(fieldData, fieldTypeInfo);
1011
+
1012
+ if (!isEntity && fieldTypeInfo.hasArguments) {
1013
+ unset(entityData, responseDataPath);
1014
+ }
1015
+
1016
+ if (isEntity) {
1017
+ promises.push(
1018
+ this._setEntityCacheEntry(keysAndPaths, { cacheability, data: entityData, fieldTypeInfo }, options, context)
986
1019
  );
987
- });
1020
+ }
1021
+
1022
+ await Promise.all(promises);
988
1023
  }
989
1024
 
990
1025
  private async _setEntityCacheEntry(
991
- { cacheability, fieldData, fieldTypeInfo }: DataForCachingEntry<EntityData>,
1026
+ { responseDataPath }: KeysAndPaths,
1027
+ { cacheability, data, fieldTypeInfo }: DataForCachingEntry,
992
1028
  options: RequestOptions,
993
1029
  context: CacheManagerContext
994
1030
  ) {
1031
+ let fieldData = get(data, responseDataPath) as EntityData;
995
1032
  const fieldTypeName = fieldTypeInfo.isEntity ? fieldTypeInfo.typeName : fieldData.__typename;
996
1033
  const entityDataKey = `${fieldTypeName}::${String(fieldData[this._typeIDKey])}`;
997
1034
  const result = await this._checkCacheEntry<EntityData>(DATA_ENTITIES, entityDataKey, options, context);
@@ -1000,7 +1037,7 @@ export class CacheManager implements CacheManagerDef {
1000
1037
  fieldData = mergeDataSets(result.entry, fieldData, this._typeIDKey);
1001
1038
  }
1002
1039
 
1003
- void this._setCacheEntry(
1040
+ await this._setCacheEntry(
1004
1041
  DATA_ENTITIES,
1005
1042
  entityDataKey,
1006
1043
  fieldData,
@@ -1008,6 +1045,8 @@ export class CacheManager implements CacheManagerDef {
1008
1045
  options,
1009
1046
  context
1010
1047
  );
1048
+
1049
+ set(data, responseDataPath, { __cacheKey: `${DATA_ENTITIES}::${entityDataKey}` });
1011
1050
  }
1012
1051
 
1013
1052
  private _setFieldCacheability(
@@ -1092,16 +1131,16 @@ export class CacheManager implements CacheManagerDef {
1092
1131
  this._partialQueryResponses.set(hash, partialQueryResponse);
1093
1132
  }
1094
1133
 
1095
- private _setQueryResponseCacheEntry(
1134
+ private async _setQueryResponseCacheEntry(
1096
1135
  hash: string,
1097
1136
  { cacheMetadata, data }: ResponseData,
1098
1137
  options: RequestOptions,
1099
1138
  context: CacheManagerContext
1100
- ): void {
1139
+ ): Promise<void> {
1101
1140
  const dehydratedCacheMetadata = dehydrateCacheMetadata(cacheMetadata);
1102
1141
  const cacheControl = CacheManager._getOperationCacheControl(cacheMetadata, context.operation);
1103
1142
 
1104
- void this._setCacheEntry(
1143
+ await this._setCacheEntry(
1105
1144
  QUERY_RESPONSES,
1106
1145
  hash,
1107
1146
  { cacheMetadata: dehydratedCacheMetadata, data },
@@ -1112,30 +1151,48 @@ export class CacheManager implements CacheManagerDef {
1112
1151
  }
1113
1152
 
1114
1153
  private async _setRequestFieldPathCacheEntry(
1154
+ field: FieldNode,
1115
1155
  keysAndPaths: KeysAndPaths,
1116
- { cacheability, fieldData }: DataForCachingEntry,
1156
+ { cacheability, data, fieldTypeInfo }: DataForCachingEntry,
1117
1157
  options: RequestOptions,
1118
1158
  context: CacheManagerContext
1119
1159
  ): Promise<void> {
1120
- const { hashedRequestFieldCacheKey, requestFieldCacheKey } = keysAndPaths;
1160
+ const { hashedRequestFieldCacheKey, requestFieldCacheKey, responseDataPath } = keysAndPaths;
1161
+ let fieldData = get(data, responseDataPath) as unknown;
1162
+ const isEntity = this._isFieldEntity(fieldData, fieldTypeInfo);
1163
+ const hasArgsOrDirectives = fieldTypeInfo.hasArguments || fieldTypeInfo.hasDirectives;
1121
1164
 
1122
- const result = await this._checkCacheEntry(REQUEST_FIELD_PATHS, hashedRequestFieldCacheKey, options, {
1123
- ...context,
1124
- requestFieldCacheKey,
1125
- });
1165
+ if (context.operation === OperationTypeNode.QUERY && (isEntity || hasArgsOrDirectives)) {
1166
+ if (isPlainObject(fieldData) && field.selectionSet?.selections) {
1167
+ fieldData = filterOutPropsWithArgsOrDirectives(fieldData, field.selectionSet.selections, keysAndPaths, context);
1168
+ }
1126
1169
 
1127
- if (result && isObjectLike(result.entry) && isObjectLike(fieldData)) {
1128
- fieldData = mergeDataSets(result.entry, fieldData, this._typeIDKey);
1129
- }
1170
+ const result = await this._checkCacheEntry(REQUEST_FIELD_PATHS, hashedRequestFieldCacheKey, options, {
1171
+ ...context,
1172
+ requestFieldCacheKey,
1173
+ });
1130
1174
 
1131
- void this._setCacheEntry(
1132
- REQUEST_FIELD_PATHS,
1133
- hashedRequestFieldCacheKey,
1134
- fieldData,
1135
- { cacheHeaders: { cacheControl: cacheability.printCacheControl() }, tag: options.tag },
1136
- options,
1137
- { ...context, requestFieldCacheKey }
1138
- );
1175
+ if (result && isObjectLike(result.entry) && isObjectLike(fieldData)) {
1176
+ fieldData = mergeDataSets(result.entry, fieldData, this._typeIDKey);
1177
+ }
1178
+
1179
+ await this._setCacheEntry(
1180
+ REQUEST_FIELD_PATHS,
1181
+ hashedRequestFieldCacheKey,
1182
+ fieldData,
1183
+ { cacheHeaders: { cacheControl: cacheability.printCacheControl() }, tag: options.tag },
1184
+ options,
1185
+ { ...context, requestFieldCacheKey }
1186
+ );
1187
+
1188
+ if (hasChildFields(field, { fragmentDefinitions: context.fragmentDefinitions })) {
1189
+ if (isEntity) {
1190
+ set(data, responseDataPath, { __cacheKey: `${REQUEST_FIELD_PATHS}::${hashedRequestFieldCacheKey}` });
1191
+ } else {
1192
+ unset(data, responseDataPath);
1193
+ }
1194
+ }
1195
+ }
1139
1196
  }
1140
1197
 
1141
1198
  private _setResponseChunksAwaitingCaching(
package/src/types.ts CHANGED
@@ -122,9 +122,9 @@ export interface ResponseDataForCaching {
122
122
  requestFieldPathData: PlainData;
123
123
  }
124
124
 
125
- export interface DataForCachingEntry<Data = unknown> {
125
+ export interface DataForCachingEntry {
126
126
  cacheability: Cacheability;
127
- fieldData: Data;
127
+ data: PlainData;
128
128
  fieldTypeInfo: FieldTypeInfo;
129
129
  }
130
130
 
@@ -157,13 +157,13 @@ export interface CacheManagerDef {
157
157
  responseData: RawResponseDataWithMaybeCacheMetadata,
158
158
  options: RequestOptions,
159
159
  context: RequestContext
160
- ): ResponseData;
160
+ ): Promise<ResponseData>;
161
161
  cacheResponse(
162
162
  requestData: RequestData,
163
163
  responseData: RawResponseDataWithMaybeCacheMetadata,
164
164
  options: RequestOptions,
165
165
  context: RequestContext
166
- ): ResponseData;
166
+ ): Promise<ResponseData>;
167
167
  checkCacheEntry(
168
168
  cacheType: CacheTypes,
169
169
  hash: string,
@@ -181,5 +181,5 @@ export interface CacheManagerDef {
181
181
  responseData: ResponseData,
182
182
  options: RequestOptions,
183
183
  context: CacheManagerContext
184
- ): void;
184
+ ): Promise<void>;
185
185
  }
@@ -1,4 +0,0 @@
1
- import type { EntityData, FieldTypeInfo } from '@graphql-box/core';
2
- import type { CacheManagerContext } from '../types.cts';
3
- export declare const createEntityDataKey: (fieldData: EntityData, fieldTypeInfo: FieldTypeInfo, context: CacheManagerContext) => string;
4
- //# sourceMappingURL=createEntityDataKey.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createEntityDataKey.d.cts","sourceRoot":"","sources":["../../../../src/helpers/createEntityDataKey.cts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,eAAO,MAAM,mBAAmB,cACnB,UAAU,iBACN,aAAa,WACnB,mBAAmB,WAI7B,CAAC"}
@@ -1,5 +0,0 @@
1
- import { type KeysAndPaths } from '@graphql-box/helpers';
2
- import { type FieldNode } from 'graphql';
3
- import { type CacheManagerContext } from '../types.cts';
4
- export declare const filterOutPropsWithEntityArgsOrDirectives: (fieldData: unknown, field: FieldNode, ancestorKeysAndPaths: KeysAndPaths, context: CacheManagerContext) => unknown;
5
- //# sourceMappingURL=filterOutPropsWithEntityArgsOrDirectives.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"filterOutPropsWithEntityArgsOrDirectives.d.cts","sourceRoot":"","sources":["../../../../src/helpers/filterOutPropsWithEntityArgsOrDirectives.cts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,eAAO,MAAM,wCAAwC,cACxC,OAAO,SACX,SAAS,wBACM,YAAY,WACzB,mBAAmB,YA2B7B,CAAC"}
@@ -1,6 +0,0 @@
1
- import { type EntityData } from '@graphql-box/core';
2
- import { type KeysAndPaths } from '@graphql-box/helpers';
3
- import { type FieldNode } from 'graphql';
4
- import { type CacheManagerContext } from '../types.cts';
5
- export declare const filterOutPropsWithEntityOrArgs: (fieldData: EntityData, field: FieldNode, ancestorKeysAndPaths: KeysAndPaths, context: CacheManagerContext) => EntityData;
6
- //# sourceMappingURL=filterOutPropsWithEntityOrArgs.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"filterOutPropsWithEntityOrArgs.d.cts","sourceRoot":"","sources":["../../../../src/helpers/filterOutPropsWithEntityOrArgs.cts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,KAAK,YAAY,EAAqD,MAAM,sBAAsB,CAAC;AAC5G,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,eAAO,MAAM,8BAA8B,cAC9B,UAAU,SACd,SAAS,wBACM,YAAY,WACzB,mBAAmB,eAmB7B,CAAC"}
@@ -1,3 +0,0 @@
1
- import type { EntityData, FieldTypeInfo } from '@graphql-box/core';
2
- export declare const isFieldEntity: (fieldData: unknown, fieldTypeInfo: FieldTypeInfo | undefined, typeIDKey: string) => fieldData is EntityData;
3
- //# sourceMappingURL=isFieldEntity.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isFieldEntity.d.cts","sourceRoot":"","sources":["../../../../src/helpers/isFieldEntity.cts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGnE,eAAO,MAAM,aAAa,cACb,OAAO,iBACH,aAAa,GAAG,SAAS,aAC7B,MAAM,4BAiBlB,CAAC"}
@@ -1,4 +0,0 @@
1
- import type { EntityData, FieldTypeInfo } from '@graphql-box/core';
2
- import type { CacheManagerContext } from '../types.ts';
3
- export declare const createEntityDataKey: (fieldData: EntityData, fieldTypeInfo: FieldTypeInfo, context: CacheManagerContext) => string;
4
- //# sourceMappingURL=createEntityDataKey.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createEntityDataKey.d.ts","sourceRoot":"","sources":["../../../../src/helpers/createEntityDataKey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,eAAO,MAAM,mBAAmB,cACnB,UAAU,iBACN,aAAa,WACnB,mBAAmB,WAI7B,CAAC"}
@@ -1,5 +0,0 @@
1
- import { type KeysAndPaths } from '@graphql-box/helpers';
2
- import { type FieldNode } from 'graphql';
3
- import { type CacheManagerContext } from '../types.ts';
4
- export declare const filterOutPropsWithEntityArgsOrDirectives: (fieldData: unknown, field: FieldNode, ancestorKeysAndPaths: KeysAndPaths, context: CacheManagerContext) => unknown;
5
- //# sourceMappingURL=filterOutPropsWithEntityArgsOrDirectives.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"filterOutPropsWithEntityArgsOrDirectives.d.ts","sourceRoot":"","sources":["../../../../src/helpers/filterOutPropsWithEntityArgsOrDirectives.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,eAAO,MAAM,wCAAwC,cACxC,OAAO,SACX,SAAS,wBACM,YAAY,WACzB,mBAAmB,YA2B7B,CAAC"}
@@ -1,6 +0,0 @@
1
- import { type EntityData } from '@graphql-box/core';
2
- import { type KeysAndPaths } from '@graphql-box/helpers';
3
- import { type FieldNode } from 'graphql';
4
- import { type CacheManagerContext } from '../types.ts';
5
- export declare const filterOutPropsWithEntityOrArgs: (fieldData: EntityData, field: FieldNode, ancestorKeysAndPaths: KeysAndPaths, context: CacheManagerContext) => EntityData;
6
- //# sourceMappingURL=filterOutPropsWithEntityOrArgs.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"filterOutPropsWithEntityOrArgs.d.ts","sourceRoot":"","sources":["../../../../src/helpers/filterOutPropsWithEntityOrArgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,KAAK,YAAY,EAAqD,MAAM,sBAAsB,CAAC;AAC5G,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,eAAO,MAAM,8BAA8B,cAC9B,UAAU,SACd,SAAS,wBACM,YAAY,WACzB,mBAAmB,eAmB7B,CAAC"}
@@ -1,3 +0,0 @@
1
- import type { EntityData, FieldTypeInfo } from '@graphql-box/core';
2
- export declare const isFieldEntity: (fieldData: unknown, fieldTypeInfo: FieldTypeInfo | undefined, typeIDKey: string) => fieldData is EntityData;
3
- //# sourceMappingURL=isFieldEntity.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isFieldEntity.d.ts","sourceRoot":"","sources":["../../../../src/helpers/isFieldEntity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGnE,eAAO,MAAM,aAAa,cACb,OAAO,iBACH,aAAa,GAAG,SAAS,aAC7B,MAAM,4BAiBlB,CAAC"}