@envelop/response-cache 5.4.0 → 5.5.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/cjs/plugin.js CHANGED
@@ -41,8 +41,9 @@ function defaultGetDocumentString(executionArgs) {
41
41
  }
42
42
  exports.defaultGetDocumentString = defaultGetDocumentString;
43
43
  const originalDocumentMap = new WeakMap();
44
- const addTypeNameToDocument = (0, utils_1.memoize2)(function addTypeNameToDocument(document, addTypeNameToDocumentOpts) {
45
- const newDocument = (0, graphql_1.visit)(document, {
44
+ const addEntityInfosToDocument = (0, utils_1.memoize4)(function addTypeNameToDocument(document, addTypeNameToDocumentOpts, schema, idFieldByTypeName) {
45
+ const typeInfo = new graphql_1.TypeInfo(schema);
46
+ const newDocument = (0, graphql_1.visit)(document, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
46
47
  OperationDefinition: {
47
48
  enter(node) {
48
49
  if (!addTypeNameToDocumentOpts.invalidateViaMutation && node.operation === 'mutation') {
@@ -53,7 +54,9 @@ const addTypeNameToDocument = (0, utils_1.memoize2)(function addTypeNameToDocume
53
54
  }
54
55
  },
55
56
  },
56
- SelectionSet(node, _key, parent) {
57
+ SelectionSet(node, _key) {
58
+ const parentType = typeInfo.getParentType();
59
+ const idField = parentType && idFieldByTypeName.get(parentType.name);
57
60
  return {
58
61
  ...node,
59
62
  selections: [
@@ -68,11 +71,20 @@ const addTypeNameToDocument = (0, utils_1.memoize2)(function addTypeNameToDocume
68
71
  value: '__responseCacheTypeName',
69
72
  },
70
73
  },
74
+ ...(idField
75
+ ? [
76
+ {
77
+ kind: graphql_1.Kind.FIELD,
78
+ name: { kind: graphql_1.Kind.NAME, value: idField },
79
+ alias: { kind: graphql_1.Kind.NAME, value: '__responseCacheId' },
80
+ },
81
+ ]
82
+ : []),
71
83
  ...node.selections,
72
84
  ],
73
85
  };
74
86
  },
75
- });
87
+ }));
76
88
  originalDocumentMap.set(newDocument, document);
77
89
  return newDocument;
78
90
  });
@@ -81,24 +93,32 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
81
93
  process.env['NODE_ENV'] === 'development' || !!process.env['DEBUG']
82
94
  : false, }) {
83
95
  const ignoredTypesMap = new Set(ignoredTypes);
84
- const processedSchemas = new WeakSet();
85
96
  const typePerSchemaCoordinateMap = new Map();
86
97
  // never cache Introspections
87
98
  ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
88
99
  const addTypeNameToDocumentOpts = { invalidateViaMutation };
100
+ const idFieldByTypeName = new Map();
101
+ let schema;
102
+ function isPrivate(typeName, data) {
103
+ if (scopePerSchemaCoordinate[typeName] === 'PRIVATE') {
104
+ return true;
105
+ }
106
+ return Object.keys(data).some(fieldName => scopePerSchemaCoordinate[`${typeName}.${fieldName}`] === 'PRIVATE');
107
+ }
89
108
  return {
90
109
  onParse() {
91
110
  return ({ result, replaceParseResult }) => {
92
111
  if (!originalDocumentMap.has(result) && result.kind === graphql_1.Kind.DOCUMENT) {
93
- const newDocument = addTypeNameToDocument(result, addTypeNameToDocumentOpts);
112
+ const newDocument = addEntityInfosToDocument(result, addTypeNameToDocumentOpts, schema, idFieldByTypeName);
94
113
  replaceParseResult(newDocument);
95
114
  }
96
115
  };
97
116
  },
98
- onSchemaChange({ schema }) {
99
- if (processedSchemas.has(schema)) {
117
+ onSchemaChange({ schema: newSchema }) {
118
+ if (schema === newSchema) {
100
119
  return;
101
120
  }
121
+ schema = newSchema;
102
122
  const cacheControlDirective = schema.getDirective('cacheControl');
103
123
  (0, utils_1.mapSchema)(schema, {
104
124
  ...(cacheControlDirective && {
@@ -120,22 +140,24 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
120
140
  const schemaCoordinates = `${typeName}.${fieldName}`;
121
141
  const resultTypeNames = unwrapTypenames(fieldConfig.type);
122
142
  typePerSchemaCoordinateMap.set(schemaCoordinates, resultTypeNames);
143
+ if (idFields.includes(fieldName) && !idFieldByTypeName.has(typeName)) {
144
+ idFieldByTypeName.set(typeName, fieldName);
145
+ }
123
146
  if (cacheControlDirective) {
124
147
  const cacheControlAnnotations = (0, utils_1.getDirective)(schema, fieldConfig, 'cacheControl');
125
148
  cacheControlAnnotations?.forEach(cacheControl => {
126
149
  const ttl = cacheControl.maxAge * 1000;
127
150
  if (ttl != null) {
128
- ttlPerSchemaCoordinate[`${typeName}.${fieldName}`] = ttl;
151
+ ttlPerSchemaCoordinate[schemaCoordinates] = ttl;
129
152
  }
130
153
  if (cacheControl.scope) {
131
- scopePerSchemaCoordinate[`${typeName}.${fieldName}`] = cacheControl.scope;
154
+ scopePerSchemaCoordinate[schemaCoordinates] = cacheControl.scope;
132
155
  }
133
156
  });
134
157
  }
135
158
  return fieldConfig;
136
159
  },
137
160
  });
138
- processedSchemas.add(schema);
139
161
  },
140
162
  async onExecute(onExecuteParams) {
141
163
  const identifier = new Map();
@@ -155,9 +177,10 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
155
177
  }
156
178
  const typename = data.__responseCacheTypeName;
157
179
  delete data.__responseCacheTypeName;
180
+ const entityId = data.__responseCacheId;
181
+ delete data.__responseCacheId;
158
182
  if (!skip) {
159
- if (ignoredTypesMap.has(typename) ||
160
- (scopePerSchemaCoordinate[typename] === 'PRIVATE' && !sessionId)) {
183
+ if (ignoredTypesMap.has(typename) || (!sessionId && isPrivate(typename, data))) {
161
184
  skip = true;
162
185
  return;
163
186
  }
@@ -165,24 +188,16 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
165
188
  if (typename in ttlPerType) {
166
189
  currentTtl = calculateTtl(ttlPerType[typename], currentTtl);
167
190
  }
191
+ if (entityId != null) {
192
+ identifier.set(`${typename}:${entityId}`, { typename, id: entityId });
193
+ }
168
194
  for (const fieldName in data) {
169
195
  const fieldData = data[fieldName];
170
- if (!skip) {
171
- if (scopePerSchemaCoordinate[`${typename}.${fieldName}`] === 'PRIVATE' &&
172
- !sessionId) {
173
- skip = true;
174
- }
175
- else if (idFields.includes(fieldName)) {
176
- const id = fieldData;
177
- identifier.set(`${typename}:${id}`, { typename, id });
178
- }
179
- else if (fieldData == null ||
180
- (Array.isArray(fieldData) && fieldData.length === 0)) {
181
- const inferredTypes = typePerSchemaCoordinateMap.get(`${typename}.${fieldName}`);
182
- inferredTypes?.forEach(inferredType => {
183
- identifier.set(inferredType, { typename: inferredType });
184
- });
185
- }
196
+ if (fieldData == null || (Array.isArray(fieldData) && fieldData.length === 0)) {
197
+ const inferredTypes = typePerSchemaCoordinateMap.get(`${typename}.${fieldName}`);
198
+ inferredTypes?.forEach(inferredType => {
199
+ identifier.set(inferredType, { typename: inferredType });
200
+ });
186
201
  }
187
202
  processResult(fieldData);
188
203
  }
package/esm/plugin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import jsonStableStringify from 'fast-json-stable-stringify';
2
2
  import { getOperationAST, Kind, print, TypeInfo, visit, visitWithTypeInfo, } from 'graphql';
3
3
  import { getDocumentString, isAsyncIterable, } from '@envelop/core';
4
- import { getDirective, MapperKind, mapSchema, memoize2, mergeIncrementalResult, } from '@graphql-tools/utils';
4
+ import { getDirective, MapperKind, mapSchema, memoize4, mergeIncrementalResult, } from '@graphql-tools/utils';
5
5
  import { hashSHA256 } from './hash-sha256.js';
6
6
  import { createInMemoryCache } from './in-memory-cache.js';
7
7
  /**
@@ -34,8 +34,9 @@ export function defaultGetDocumentString(executionArgs) {
34
34
  return getDocumentString(executionArgs.document, print);
35
35
  }
36
36
  const originalDocumentMap = new WeakMap();
37
- const addTypeNameToDocument = memoize2(function addTypeNameToDocument(document, addTypeNameToDocumentOpts) {
38
- const newDocument = visit(document, {
37
+ const addEntityInfosToDocument = memoize4(function addTypeNameToDocument(document, addTypeNameToDocumentOpts, schema, idFieldByTypeName) {
38
+ const typeInfo = new TypeInfo(schema);
39
+ const newDocument = visit(document, visitWithTypeInfo(typeInfo, {
39
40
  OperationDefinition: {
40
41
  enter(node) {
41
42
  if (!addTypeNameToDocumentOpts.invalidateViaMutation && node.operation === 'mutation') {
@@ -46,7 +47,9 @@ const addTypeNameToDocument = memoize2(function addTypeNameToDocument(document,
46
47
  }
47
48
  },
48
49
  },
49
- SelectionSet(node, _key, parent) {
50
+ SelectionSet(node, _key) {
51
+ const parentType = typeInfo.getParentType();
52
+ const idField = parentType && idFieldByTypeName.get(parentType.name);
50
53
  return {
51
54
  ...node,
52
55
  selections: [
@@ -61,11 +64,20 @@ const addTypeNameToDocument = memoize2(function addTypeNameToDocument(document,
61
64
  value: '__responseCacheTypeName',
62
65
  },
63
66
  },
67
+ ...(idField
68
+ ? [
69
+ {
70
+ kind: Kind.FIELD,
71
+ name: { kind: Kind.NAME, value: idField },
72
+ alias: { kind: Kind.NAME, value: '__responseCacheId' },
73
+ },
74
+ ]
75
+ : []),
64
76
  ...node.selections,
65
77
  ],
66
78
  };
67
79
  },
68
- });
80
+ }));
69
81
  originalDocumentMap.set(newDocument, document);
70
82
  return newDocument;
71
83
  });
@@ -74,24 +86,32 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
74
86
  process.env['NODE_ENV'] === 'development' || !!process.env['DEBUG']
75
87
  : false, }) {
76
88
  const ignoredTypesMap = new Set(ignoredTypes);
77
- const processedSchemas = new WeakSet();
78
89
  const typePerSchemaCoordinateMap = new Map();
79
90
  // never cache Introspections
80
91
  ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
81
92
  const addTypeNameToDocumentOpts = { invalidateViaMutation };
93
+ const idFieldByTypeName = new Map();
94
+ let schema;
95
+ function isPrivate(typeName, data) {
96
+ if (scopePerSchemaCoordinate[typeName] === 'PRIVATE') {
97
+ return true;
98
+ }
99
+ return Object.keys(data).some(fieldName => scopePerSchemaCoordinate[`${typeName}.${fieldName}`] === 'PRIVATE');
100
+ }
82
101
  return {
83
102
  onParse() {
84
103
  return ({ result, replaceParseResult }) => {
85
104
  if (!originalDocumentMap.has(result) && result.kind === Kind.DOCUMENT) {
86
- const newDocument = addTypeNameToDocument(result, addTypeNameToDocumentOpts);
105
+ const newDocument = addEntityInfosToDocument(result, addTypeNameToDocumentOpts, schema, idFieldByTypeName);
87
106
  replaceParseResult(newDocument);
88
107
  }
89
108
  };
90
109
  },
91
- onSchemaChange({ schema }) {
92
- if (processedSchemas.has(schema)) {
110
+ onSchemaChange({ schema: newSchema }) {
111
+ if (schema === newSchema) {
93
112
  return;
94
113
  }
114
+ schema = newSchema;
95
115
  const cacheControlDirective = schema.getDirective('cacheControl');
96
116
  mapSchema(schema, {
97
117
  ...(cacheControlDirective && {
@@ -113,22 +133,24 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
113
133
  const schemaCoordinates = `${typeName}.${fieldName}`;
114
134
  const resultTypeNames = unwrapTypenames(fieldConfig.type);
115
135
  typePerSchemaCoordinateMap.set(schemaCoordinates, resultTypeNames);
136
+ if (idFields.includes(fieldName) && !idFieldByTypeName.has(typeName)) {
137
+ idFieldByTypeName.set(typeName, fieldName);
138
+ }
116
139
  if (cacheControlDirective) {
117
140
  const cacheControlAnnotations = getDirective(schema, fieldConfig, 'cacheControl');
118
141
  cacheControlAnnotations?.forEach(cacheControl => {
119
142
  const ttl = cacheControl.maxAge * 1000;
120
143
  if (ttl != null) {
121
- ttlPerSchemaCoordinate[`${typeName}.${fieldName}`] = ttl;
144
+ ttlPerSchemaCoordinate[schemaCoordinates] = ttl;
122
145
  }
123
146
  if (cacheControl.scope) {
124
- scopePerSchemaCoordinate[`${typeName}.${fieldName}`] = cacheControl.scope;
147
+ scopePerSchemaCoordinate[schemaCoordinates] = cacheControl.scope;
125
148
  }
126
149
  });
127
150
  }
128
151
  return fieldConfig;
129
152
  },
130
153
  });
131
- processedSchemas.add(schema);
132
154
  },
133
155
  async onExecute(onExecuteParams) {
134
156
  const identifier = new Map();
@@ -148,9 +170,10 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
148
170
  }
149
171
  const typename = data.__responseCacheTypeName;
150
172
  delete data.__responseCacheTypeName;
173
+ const entityId = data.__responseCacheId;
174
+ delete data.__responseCacheId;
151
175
  if (!skip) {
152
- if (ignoredTypesMap.has(typename) ||
153
- (scopePerSchemaCoordinate[typename] === 'PRIVATE' && !sessionId)) {
176
+ if (ignoredTypesMap.has(typename) || (!sessionId && isPrivate(typename, data))) {
154
177
  skip = true;
155
178
  return;
156
179
  }
@@ -158,24 +181,16 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
158
181
  if (typename in ttlPerType) {
159
182
  currentTtl = calculateTtl(ttlPerType[typename], currentTtl);
160
183
  }
184
+ if (entityId != null) {
185
+ identifier.set(`${typename}:${entityId}`, { typename, id: entityId });
186
+ }
161
187
  for (const fieldName in data) {
162
188
  const fieldData = data[fieldName];
163
- if (!skip) {
164
- if (scopePerSchemaCoordinate[`${typename}.${fieldName}`] === 'PRIVATE' &&
165
- !sessionId) {
166
- skip = true;
167
- }
168
- else if (idFields.includes(fieldName)) {
169
- const id = fieldData;
170
- identifier.set(`${typename}:${id}`, { typename, id });
171
- }
172
- else if (fieldData == null ||
173
- (Array.isArray(fieldData) && fieldData.length === 0)) {
174
- const inferredTypes = typePerSchemaCoordinateMap.get(`${typename}.${fieldName}`);
175
- inferredTypes?.forEach(inferredType => {
176
- identifier.set(inferredType, { typename: inferredType });
177
- });
178
- }
189
+ if (fieldData == null || (Array.isArray(fieldData) && fieldData.length === 0)) {
190
+ const inferredTypes = typePerSchemaCoordinateMap.get(`${typename}.${fieldName}`);
191
+ inferredTypes?.forEach(inferredType => {
192
+ identifier.set(inferredType, { typename: inferredType });
193
+ });
179
194
  }
180
195
  processResult(fieldData);
181
196
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envelop/response-cache",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "@envelop/core": "^4.0.3",