@envelop/response-cache 3.0.0-alpha-4ba70ab.0 → 3.0.0-alpha-d0d0776.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 (4) hide show
  1. package/README.md +12 -0
  2. package/index.js +104 -113
  3. package/index.mjs +96 -108
  4. package/package.json +19 -7
package/README.md CHANGED
@@ -8,6 +8,18 @@
8
8
 
9
9
  [Check out the GraphQL Response Cache Guide for more information](https://envelop.dev/docs/guides/adding-a-graphql-response-cache)
10
10
 
11
+ > Watch [Episode #34 of `graphql.wtf`](https://graphql.wtf/episodes/34-response-cache-plugin-with-envelop) for a quick introduction to using Response Cache plugin with Envelop:
12
+
13
+ <iframe
14
+ width="100%"
15
+ height="400"
16
+ src="https://www.youtube.com/embed/1EBphPltkA4"
17
+ title="YouTube video player"
18
+ frameBorder="0"
19
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
20
+ allowFullScreen
21
+ ></iframe>
22
+
11
23
  ## Getting Started
12
24
 
13
25
  ```bash
package/index.js CHANGED
@@ -2,19 +2,21 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
-
7
- const LRU = _interopDefault(require('lru-cache'));
5
+ const LRU = require('lru-cache');
8
6
  const core = require('@envelop/core');
9
7
  const utils = require('@graphql-tools/utils');
10
8
  const graphql = require('graphql');
11
- const jsonStableStringify = _interopDefault(require('fast-json-stable-stringify'));
9
+ const jsonStableStringify = require('fast-json-stable-stringify');
10
+
11
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
+
13
+ const LRU__default = /*#__PURE__*/_interopDefaultLegacy(LRU);
14
+ const jsonStableStringify__default = /*#__PURE__*/_interopDefaultLegacy(jsonStableStringify);
12
15
 
13
16
  const createInMemoryCache = (params) => {
14
- var _a;
15
- const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : defaultBuildEntityId;
16
- const cachedResponses = new LRU({
17
- max: params === null || params === void 0 ? void 0 : params.max,
17
+ const buildEntityId = params?.buildEntityId ?? defaultBuildEntityId;
18
+ const cachedResponses = new LRU__default["default"]({
19
+ max: params?.max,
18
20
  stale: false,
19
21
  noDisposeOnSet: true,
20
22
  dispose(responseId) {
@@ -24,13 +26,12 @@ const createInMemoryCache = (params) => {
24
26
  const entityToResponseIds = new Map();
25
27
  const responseIdToEntityIds = new Map();
26
28
  function purgeResponse(responseId, shouldRemove = true) {
27
- var _a;
28
29
  const entityIds = responseIdToEntityIds.get(responseId);
29
30
  // get entities related to the response
30
31
  if (entityIds !== undefined) {
31
32
  for (const entityId of entityIds) {
32
33
  // remove the response mapping from the entity
33
- (_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
34
+ entityToResponseIds.get(entityId)?.delete(responseId);
34
35
  }
35
36
  // remove all the entity mappings from the response
36
37
  responseIdToEntityIds.delete(responseId);
@@ -78,8 +79,7 @@ const createInMemoryCache = (params) => {
78
79
  }
79
80
  },
80
81
  get(responseId) {
81
- var _a;
82
- return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
82
+ return cachedResponses.get(responseId) ?? null;
83
83
  },
84
84
  invalidate(entitiesToRemove) {
85
85
  for (const { typename, id } of entitiesToRemove) {
@@ -124,7 +124,7 @@ const hashSHA256 = async (text) => {
124
124
  if (nodeCrypto) {
125
125
  return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
126
126
  }
127
- else if (webCrypto) {
127
+ if (webCrypto) {
128
128
  let buf;
129
129
  if (typeof TextEncoder !== 'undefined') {
130
130
  buf = new TextEncoder().encode(text);
@@ -159,15 +159,12 @@ const rawDocumentStringSymbol = Symbol('rawDocumentString');
159
159
  * Default function used for building the response cache key.
160
160
  * It is exported here for advanced use-cases. E.g. if you want to short circuit and serve responses from the cache on a global level in order to completely by-pass the GraphQL flow.
161
161
  */
162
- const defaultBuildResponseCacheKey = params => {
163
- var _a, _b, _c;
164
- return hashSHA256([
165
- params.documentString,
166
- (_a = params.operationName) !== null && _a !== void 0 ? _a : '',
167
- jsonStableStringify((_b = params.variableValues) !== null && _b !== void 0 ? _b : {}),
168
- (_c = params.sessionId) !== null && _c !== void 0 ? _c : '',
169
- ].join('|'));
170
- };
162
+ const defaultBuildResponseCacheKey = params => hashSHA256([
163
+ params.documentString,
164
+ params.operationName ?? '',
165
+ jsonStableStringify__default["default"](params.variableValues ?? {}),
166
+ params.sessionId ?? '',
167
+ ].join('|'));
171
168
  /**
172
169
  * Default function used to check if the result should be cached.
173
170
  *
@@ -188,18 +185,22 @@ const defaultGetDocumentStringFromContext = context => context[rawDocumentString
188
185
  function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl = Infinity, session = () => null, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = defaultBuildResponseCacheKey, getDocumentStringFromContext = defaultGetDocumentStringFromContext, shouldCacheResult = defaultShouldCacheResult,
189
186
  // eslint-disable-next-line dot-notation
190
187
  includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false, } = {}) {
188
+ const appliedTransform = Symbol('responseCache.appliedTransform');
191
189
  const ignoredTypesMap = new Set(ignoredTypes);
192
- const schemaCache = new WeakMap();
193
190
  // never cache Introspections
194
191
  ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
195
192
  return {
196
- onSchemaChange(ctx) {
197
- let schema = schemaCache.get(ctx.schema);
198
- if (schema == null) {
199
- schema = applyResponseCacheLogic(ctx.schema, idFields);
200
- schemaCache.set(ctx.schema, schema);
193
+ onSchemaChange({ schema, replaceSchema }) {
194
+ // @ts-expect-error See https://github.com/graphql/graphql-js/pull/3511 - remove this comments once merged
195
+ if (schema.extensions?.[appliedTransform] === true) {
196
+ return;
201
197
  }
202
- ctx.replaceSchema(schema);
198
+ const patchedSchema = applyResponseCacheLogic(schema, idFields);
199
+ patchedSchema.extensions = {
200
+ ...patchedSchema.extensions,
201
+ [appliedTransform]: true,
202
+ };
203
+ replaceSchema(patchedSchema);
203
204
  },
204
205
  onParse(parseCtx) {
205
206
  return function onParseEnd(ctx) {
@@ -213,7 +214,6 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
213
214
  };
214
215
  },
215
216
  async onExecute(ctx) {
216
- var _a;
217
217
  const identifier = new Map();
218
218
  const types = new Set();
219
219
  const context = {
@@ -253,107 +253,99 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
253
253
  },
254
254
  };
255
255
  }
256
- else {
257
- const documentString = getDocumentStringFromContext(ctx.args.contextValue);
258
- if (documentString != null) {
259
- const operationId = await buildResponseCacheKey({
260
- documentString,
261
- variableValues: ctx.args.variableValues,
262
- operationName: ctx.args.operationName,
263
- sessionId: session(ctx.args.contextValue),
264
- });
265
- if (((_a = enabled === null || enabled === void 0 ? void 0 : enabled(ctx.args.contextValue)) !== null && _a !== void 0 ? _a : true) === true) {
266
- const cachedResponse = await cache.get(operationId);
267
- if (cachedResponse != null) {
268
- if (includeExtensionMetadata) {
269
- ctx.setResultAndStopExecution({
270
- ...cachedResponse,
271
- extensions: {
272
- responseCache: {
273
- hit: true,
274
- },
256
+ const documentString = getDocumentStringFromContext(ctx.args.contextValue);
257
+ if (documentString != null) {
258
+ const operationId = await buildResponseCacheKey({
259
+ documentString,
260
+ variableValues: ctx.args.variableValues,
261
+ operationName: ctx.args.operationName,
262
+ sessionId: session(ctx.args.contextValue),
263
+ });
264
+ if ((enabled?.(ctx.args.contextValue) ?? true) === true) {
265
+ const cachedResponse = await cache.get(operationId);
266
+ if (cachedResponse != null) {
267
+ if (includeExtensionMetadata) {
268
+ ctx.setResultAndStopExecution({
269
+ ...cachedResponse,
270
+ extensions: {
271
+ responseCache: {
272
+ hit: true,
275
273
  },
276
- });
277
- }
278
- else {
279
- ctx.setResultAndStopExecution(cachedResponse);
280
- }
281
- return;
274
+ },
275
+ });
282
276
  }
277
+ else {
278
+ ctx.setResultAndStopExecution(cachedResponse);
279
+ }
280
+ return;
283
281
  }
284
- if (ttlPerSchemaCoordinate) {
285
- const typeInfo = new graphql.TypeInfo(ctx.args.schema);
286
- graphql.visit(ctx.args.document, graphql.visitWithTypeInfo(typeInfo, {
287
- Field(fieldNode) {
288
- const parentType = typeInfo.getParentType();
289
- if (parentType) {
290
- const schemaCoordinate = `${parentType.name}.${fieldNode.name.value}`;
291
- const maybeTtl = ttlPerSchemaCoordinate[schemaCoordinate];
292
- if (maybeTtl !== undefined) {
293
- context.currentTtl = calculateTtl(maybeTtl, context.currentTtl);
294
- }
295
- }
296
- },
297
- }));
298
- }
299
- return {
300
- onExecuteDone({ result, setResult }) {
301
- var _a;
302
- if (core.isAsyncIterable(result)) {
303
- // eslint-disable-next-line no-console
304
- console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
305
- return;
306
- }
307
- if (context.skip) {
308
- return;
309
- }
310
- if (!shouldCacheResult({ result })) {
311
- return;
312
- }
313
- // we only use the global ttl if no currentTtl has been determined.
314
- const finalTtl = (_a = context.currentTtl) !== null && _a !== void 0 ? _a : globalTtl;
315
- if (finalTtl === 0) {
316
- if (includeExtensionMetadata) {
317
- setResult({
318
- ...result,
319
- extensions: {
320
- responseCache: {
321
- hit: false,
322
- didCache: false,
323
- },
324
- },
325
- });
282
+ }
283
+ if (ttlPerSchemaCoordinate) {
284
+ const typeInfo = new graphql.TypeInfo(ctx.args.schema);
285
+ graphql.visit(ctx.args.document, graphql.visitWithTypeInfo(typeInfo, {
286
+ Field(fieldNode) {
287
+ const parentType = typeInfo.getParentType();
288
+ if (parentType) {
289
+ const schemaCoordinate = `${parentType.name}.${fieldNode.name.value}`;
290
+ const maybeTtl = ttlPerSchemaCoordinate[schemaCoordinate];
291
+ if (maybeTtl !== undefined) {
292
+ context.currentTtl = calculateTtl(maybeTtl, context.currentTtl);
326
293
  }
327
- return;
328
294
  }
329
- cache.set(operationId, result, identifier.values(), finalTtl);
295
+ },
296
+ }));
297
+ }
298
+ return {
299
+ onExecuteDone({ result, setResult }) {
300
+ if (core.isAsyncIterable(result)) {
301
+ // eslint-disable-next-line no-console
302
+ console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
303
+ return;
304
+ }
305
+ if (!shouldCacheResult({ result })) {
306
+ return;
307
+ }
308
+ // we only use the global ttl if no currentTtl has been determined.
309
+ const finalTtl = context.currentTtl ?? globalTtl;
310
+ if (finalTtl === 0) {
330
311
  if (includeExtensionMetadata) {
331
312
  setResult({
332
313
  ...result,
333
314
  extensions: {
334
315
  responseCache: {
335
316
  hit: false,
336
- didCache: true,
337
- ttl: finalTtl,
317
+ didCache: false,
338
318
  },
339
319
  },
340
320
  });
341
321
  }
342
- },
343
- };
344
- }
345
- else {
346
- // eslint-disable-next-line no-console
347
- console.warn(`[useResponseCache] Failed extracting document string from the context. The response will not be cached or served from the cache. ` +
348
- `If you are overriding the 'parse' behavior make sure to pass a custom 'getDocumentStringFromContext' function for getting the document string, which is required for building the response cache key.`);
349
- return undefined;
350
- }
322
+ return;
323
+ }
324
+ cache.set(operationId, result, identifier.values(), finalTtl);
325
+ if (includeExtensionMetadata) {
326
+ setResult({
327
+ ...result,
328
+ extensions: {
329
+ responseCache: {
330
+ hit: false,
331
+ didCache: true,
332
+ ttl: finalTtl,
333
+ },
334
+ },
335
+ });
336
+ }
337
+ },
338
+ };
351
339
  }
340
+ // eslint-disable-next-line no-console
341
+ console.warn(`[useResponseCache] Failed extracting document string from the context. The response will not be cached or served from the cache. ` +
342
+ `If you are overriding the 'parse' behavior make sure to pass a custom 'getDocumentStringFromContext' function for getting the document string, which is required for building the response cache key.`);
343
+ return undefined;
352
344
  },
353
345
  };
354
346
  }
355
347
  function isOperationDefinition(node) {
356
- return (node === null || node === void 0 ? void 0 : node.kind) === 'OperationDefinition';
348
+ return node?.kind === 'OperationDefinition';
357
349
  }
358
350
  function calculateTtl(typeTtl, currentTtl) {
359
351
  if (typeof currentTtl === 'number') {
@@ -379,8 +371,7 @@ function applyResponseCacheLogic(schema, idFieldNames) {
379
371
  return {
380
372
  ...fieldConfig,
381
373
  resolve(src, args, context, info) {
382
- var _a;
383
- const result = ((_a = fieldConfig.resolve) !== null && _a !== void 0 ? _a : graphql.defaultFieldResolver)(src, args, context, info);
374
+ const result = (fieldConfig.resolve ?? graphql.defaultFieldResolver)(src, args, context, info);
384
375
  runWith(result, (id) => {
385
376
  const ctx = context[contextSymbol];
386
377
  if (ctx !== undefined) {
package/index.mjs CHANGED
@@ -5,10 +5,9 @@ import { defaultFieldResolver, TypeInfo, visit, visitWithTypeInfo } from 'graphq
5
5
  import jsonStableStringify from 'fast-json-stable-stringify';
6
6
 
7
7
  const createInMemoryCache = (params) => {
8
- var _a;
9
- const buildEntityId = (_a = params === null || params === void 0 ? void 0 : params.buildEntityId) !== null && _a !== void 0 ? _a : defaultBuildEntityId;
8
+ const buildEntityId = params?.buildEntityId ?? defaultBuildEntityId;
10
9
  const cachedResponses = new LRU({
11
- max: params === null || params === void 0 ? void 0 : params.max,
10
+ max: params?.max,
12
11
  stale: false,
13
12
  noDisposeOnSet: true,
14
13
  dispose(responseId) {
@@ -18,13 +17,12 @@ const createInMemoryCache = (params) => {
18
17
  const entityToResponseIds = new Map();
19
18
  const responseIdToEntityIds = new Map();
20
19
  function purgeResponse(responseId, shouldRemove = true) {
21
- var _a;
22
20
  const entityIds = responseIdToEntityIds.get(responseId);
23
21
  // get entities related to the response
24
22
  if (entityIds !== undefined) {
25
23
  for (const entityId of entityIds) {
26
24
  // remove the response mapping from the entity
27
- (_a = entityToResponseIds.get(entityId)) === null || _a === void 0 ? void 0 : _a.delete(responseId);
25
+ entityToResponseIds.get(entityId)?.delete(responseId);
28
26
  }
29
27
  // remove all the entity mappings from the response
30
28
  responseIdToEntityIds.delete(responseId);
@@ -72,8 +70,7 @@ const createInMemoryCache = (params) => {
72
70
  }
73
71
  },
74
72
  get(responseId) {
75
- var _a;
76
- return (_a = cachedResponses.get(responseId)) !== null && _a !== void 0 ? _a : null;
73
+ return cachedResponses.get(responseId) ?? null;
77
74
  },
78
75
  invalidate(entitiesToRemove) {
79
76
  for (const { typename, id } of entitiesToRemove) {
@@ -118,7 +115,7 @@ const hashSHA256 = async (text) => {
118
115
  if (nodeCrypto) {
119
116
  return nodeCrypto.then(crypto => crypto.createHash('sha256').update(text).digest('hex'));
120
117
  }
121
- else if (webCrypto) {
118
+ if (webCrypto) {
122
119
  let buf;
123
120
  if (typeof TextEncoder !== 'undefined') {
124
121
  buf = new TextEncoder().encode(text);
@@ -153,15 +150,12 @@ const rawDocumentStringSymbol = Symbol('rawDocumentString');
153
150
  * Default function used for building the response cache key.
154
151
  * It is exported here for advanced use-cases. E.g. if you want to short circuit and serve responses from the cache on a global level in order to completely by-pass the GraphQL flow.
155
152
  */
156
- const defaultBuildResponseCacheKey = params => {
157
- var _a, _b, _c;
158
- return hashSHA256([
159
- params.documentString,
160
- (_a = params.operationName) !== null && _a !== void 0 ? _a : '',
161
- jsonStableStringify((_b = params.variableValues) !== null && _b !== void 0 ? _b : {}),
162
- (_c = params.sessionId) !== null && _c !== void 0 ? _c : '',
163
- ].join('|'));
164
- };
153
+ const defaultBuildResponseCacheKey = params => hashSHA256([
154
+ params.documentString,
155
+ params.operationName ?? '',
156
+ jsonStableStringify(params.variableValues ?? {}),
157
+ params.sessionId ?? '',
158
+ ].join('|'));
165
159
  /**
166
160
  * Default function used to check if the result should be cached.
167
161
  *
@@ -182,18 +176,22 @@ const defaultGetDocumentStringFromContext = context => context[rawDocumentString
182
176
  function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl = Infinity, session = () => null, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = defaultBuildResponseCacheKey, getDocumentStringFromContext = defaultGetDocumentStringFromContext, shouldCacheResult = defaultShouldCacheResult,
183
177
  // eslint-disable-next-line dot-notation
184
178
  includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_ENV'] === 'development' : false, } = {}) {
179
+ const appliedTransform = Symbol('responseCache.appliedTransform');
185
180
  const ignoredTypesMap = new Set(ignoredTypes);
186
- const schemaCache = new WeakMap();
187
181
  // never cache Introspections
188
182
  ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
189
183
  return {
190
- onSchemaChange(ctx) {
191
- let schema = schemaCache.get(ctx.schema);
192
- if (schema == null) {
193
- schema = applyResponseCacheLogic(ctx.schema, idFields);
194
- schemaCache.set(ctx.schema, schema);
184
+ onSchemaChange({ schema, replaceSchema }) {
185
+ // @ts-expect-error See https://github.com/graphql/graphql-js/pull/3511 - remove this comments once merged
186
+ if (schema.extensions?.[appliedTransform] === true) {
187
+ return;
195
188
  }
196
- ctx.replaceSchema(schema);
189
+ const patchedSchema = applyResponseCacheLogic(schema, idFields);
190
+ patchedSchema.extensions = {
191
+ ...patchedSchema.extensions,
192
+ [appliedTransform]: true,
193
+ };
194
+ replaceSchema(patchedSchema);
197
195
  },
198
196
  onParse(parseCtx) {
199
197
  return function onParseEnd(ctx) {
@@ -207,7 +205,6 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
207
205
  };
208
206
  },
209
207
  async onExecute(ctx) {
210
- var _a;
211
208
  const identifier = new Map();
212
209
  const types = new Set();
213
210
  const context = {
@@ -247,107 +244,99 @@ includeExtensionMetadata = typeof process !== 'undefined' ? process.env['NODE_EN
247
244
  },
248
245
  };
249
246
  }
250
- else {
251
- const documentString = getDocumentStringFromContext(ctx.args.contextValue);
252
- if (documentString != null) {
253
- const operationId = await buildResponseCacheKey({
254
- documentString,
255
- variableValues: ctx.args.variableValues,
256
- operationName: ctx.args.operationName,
257
- sessionId: session(ctx.args.contextValue),
258
- });
259
- if (((_a = enabled === null || enabled === void 0 ? void 0 : enabled(ctx.args.contextValue)) !== null && _a !== void 0 ? _a : true) === true) {
260
- const cachedResponse = await cache.get(operationId);
261
- if (cachedResponse != null) {
262
- if (includeExtensionMetadata) {
263
- ctx.setResultAndStopExecution({
264
- ...cachedResponse,
265
- extensions: {
266
- responseCache: {
267
- hit: true,
268
- },
247
+ const documentString = getDocumentStringFromContext(ctx.args.contextValue);
248
+ if (documentString != null) {
249
+ const operationId = await buildResponseCacheKey({
250
+ documentString,
251
+ variableValues: ctx.args.variableValues,
252
+ operationName: ctx.args.operationName,
253
+ sessionId: session(ctx.args.contextValue),
254
+ });
255
+ if ((enabled?.(ctx.args.contextValue) ?? true) === true) {
256
+ const cachedResponse = await cache.get(operationId);
257
+ if (cachedResponse != null) {
258
+ if (includeExtensionMetadata) {
259
+ ctx.setResultAndStopExecution({
260
+ ...cachedResponse,
261
+ extensions: {
262
+ responseCache: {
263
+ hit: true,
269
264
  },
270
- });
271
- }
272
- else {
273
- ctx.setResultAndStopExecution(cachedResponse);
274
- }
275
- return;
265
+ },
266
+ });
276
267
  }
268
+ else {
269
+ ctx.setResultAndStopExecution(cachedResponse);
270
+ }
271
+ return;
277
272
  }
278
- if (ttlPerSchemaCoordinate) {
279
- const typeInfo = new TypeInfo(ctx.args.schema);
280
- visit(ctx.args.document, visitWithTypeInfo(typeInfo, {
281
- Field(fieldNode) {
282
- const parentType = typeInfo.getParentType();
283
- if (parentType) {
284
- const schemaCoordinate = `${parentType.name}.${fieldNode.name.value}`;
285
- const maybeTtl = ttlPerSchemaCoordinate[schemaCoordinate];
286
- if (maybeTtl !== undefined) {
287
- context.currentTtl = calculateTtl(maybeTtl, context.currentTtl);
288
- }
289
- }
290
- },
291
- }));
292
- }
293
- return {
294
- onExecuteDone({ result, setResult }) {
295
- var _a;
296
- if (isAsyncIterable(result)) {
297
- // eslint-disable-next-line no-console
298
- console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
299
- return;
300
- }
301
- if (context.skip) {
302
- return;
303
- }
304
- if (!shouldCacheResult({ result })) {
305
- return;
306
- }
307
- // we only use the global ttl if no currentTtl has been determined.
308
- const finalTtl = (_a = context.currentTtl) !== null && _a !== void 0 ? _a : globalTtl;
309
- if (finalTtl === 0) {
310
- if (includeExtensionMetadata) {
311
- setResult({
312
- ...result,
313
- extensions: {
314
- responseCache: {
315
- hit: false,
316
- didCache: false,
317
- },
318
- },
319
- });
273
+ }
274
+ if (ttlPerSchemaCoordinate) {
275
+ const typeInfo = new TypeInfo(ctx.args.schema);
276
+ visit(ctx.args.document, visitWithTypeInfo(typeInfo, {
277
+ Field(fieldNode) {
278
+ const parentType = typeInfo.getParentType();
279
+ if (parentType) {
280
+ const schemaCoordinate = `${parentType.name}.${fieldNode.name.value}`;
281
+ const maybeTtl = ttlPerSchemaCoordinate[schemaCoordinate];
282
+ if (maybeTtl !== undefined) {
283
+ context.currentTtl = calculateTtl(maybeTtl, context.currentTtl);
320
284
  }
321
- return;
322
285
  }
323
- cache.set(operationId, result, identifier.values(), finalTtl);
286
+ },
287
+ }));
288
+ }
289
+ return {
290
+ onExecuteDone({ result, setResult }) {
291
+ if (isAsyncIterable(result)) {
292
+ // eslint-disable-next-line no-console
293
+ console.warn('[useResponseCache] AsyncIterable returned from execute is currently unsupported.');
294
+ return;
295
+ }
296
+ if (!shouldCacheResult({ result })) {
297
+ return;
298
+ }
299
+ // we only use the global ttl if no currentTtl has been determined.
300
+ const finalTtl = context.currentTtl ?? globalTtl;
301
+ if (finalTtl === 0) {
324
302
  if (includeExtensionMetadata) {
325
303
  setResult({
326
304
  ...result,
327
305
  extensions: {
328
306
  responseCache: {
329
307
  hit: false,
330
- didCache: true,
331
- ttl: finalTtl,
308
+ didCache: false,
332
309
  },
333
310
  },
334
311
  });
335
312
  }
336
- },
337
- };
338
- }
339
- else {
340
- // eslint-disable-next-line no-console
341
- console.warn(`[useResponseCache] Failed extracting document string from the context. The response will not be cached or served from the cache. ` +
342
- `If you are overriding the 'parse' behavior make sure to pass a custom 'getDocumentStringFromContext' function for getting the document string, which is required for building the response cache key.`);
343
- return undefined;
344
- }
313
+ return;
314
+ }
315
+ cache.set(operationId, result, identifier.values(), finalTtl);
316
+ if (includeExtensionMetadata) {
317
+ setResult({
318
+ ...result,
319
+ extensions: {
320
+ responseCache: {
321
+ hit: false,
322
+ didCache: true,
323
+ ttl: finalTtl,
324
+ },
325
+ },
326
+ });
327
+ }
328
+ },
329
+ };
345
330
  }
331
+ // eslint-disable-next-line no-console
332
+ console.warn(`[useResponseCache] Failed extracting document string from the context. The response will not be cached or served from the cache. ` +
333
+ `If you are overriding the 'parse' behavior make sure to pass a custom 'getDocumentStringFromContext' function for getting the document string, which is required for building the response cache key.`);
334
+ return undefined;
346
335
  },
347
336
  };
348
337
  }
349
338
  function isOperationDefinition(node) {
350
- return (node === null || node === void 0 ? void 0 : node.kind) === 'OperationDefinition';
339
+ return node?.kind === 'OperationDefinition';
351
340
  }
352
341
  function calculateTtl(typeTtl, currentTtl) {
353
342
  if (typeof currentTtl === 'number') {
@@ -373,8 +362,7 @@ function applyResponseCacheLogic(schema, idFieldNames) {
373
362
  return {
374
363
  ...fieldConfig,
375
364
  resolve(src, args, context, info) {
376
- var _a;
377
- const result = ((_a = fieldConfig.resolve) !== null && _a !== void 0 ? _a : defaultFieldResolver)(src, args, context, info);
365
+ const result = (fieldConfig.resolve ?? defaultFieldResolver)(src, args, context, info);
378
366
  runWith(result, (id) => {
379
367
  const ctx = context[contextSymbol];
380
368
  if (ctx !== undefined) {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@envelop/response-cache",
3
- "version": "3.0.0-alpha-4ba70ab.0",
3
+ "version": "3.0.0-alpha-d0d0776.0",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
- "@envelop/core": "2.1.0-alpha-4ba70ab.0",
6
+ "@envelop/core": "3.0.0-alpha-d0d0776.0",
7
7
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
8
8
  },
9
9
  "dependencies": {
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "https://github.com/dotansimha/envelop.git",
16
+ "url": "https://github.com/n1ru4l/envelop.git",
17
17
  "directory": "packages/plugins/response-cache"
18
18
  },
19
19
  "author": "Kamil Kisiela <kamilkisiela@gmail.com>",
@@ -26,12 +26,24 @@
26
26
  },
27
27
  "exports": {
28
28
  ".": {
29
- "require": "./index.js",
30
- "import": "./index.mjs"
29
+ "require": {
30
+ "default": "./index.js",
31
+ "types": "./index.d.ts"
32
+ },
33
+ "import": {
34
+ "default": "./index.mjs",
35
+ "types": "./index.d.ts"
36
+ }
31
37
  },
32
38
  "./*": {
33
- "require": "./*.js",
34
- "import": "./*.mjs"
39
+ "require": {
40
+ "default": "./*.js",
41
+ "types": "./*.d.ts"
42
+ },
43
+ "import": {
44
+ "default": "./*.mjs",
45
+ "types": "./*.d.ts"
46
+ }
35
47
  },
36
48
  "./package.json": "./package.json"
37
49
  }