@envelop/response-cache 6.1.0 → 6.1.1-alpha-20231123130839-6a4e30b3

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/README.md CHANGED
@@ -153,6 +153,60 @@ const getEnveloped = envelop({
153
153
  > Note: In the Recipes below, be sure to provide your Redis `cache` instance with
154
154
  > `useResponseCache({ cache })`.
155
155
 
156
+ ### Cloudflare KV Cache
157
+
158
+ ```bash
159
+ yarn add @envelop/response-cache-cloudflare-kv
160
+ ```
161
+
162
+ In order to use the Cloudflare KV cache, you need to:
163
+
164
+ - Create a Cloudflare KV namespace
165
+ - Add that namespace to your `wrangler.toml` in order to access it from your worker. Read the
166
+ [KV docs](https://developers.cloudflare.com/kv/get-started/) to get started.
167
+ - Pass the KV namespace to the `createKvCache` function and set to the `useResponseCache` plugin
168
+ options. See the example below.
169
+
170
+ The example below demonstrates how to use this with graphql-yoga within a Cloudflare Worker script.
171
+
172
+ ```ts
173
+ import { createSchema, createYoga, YogaInitialContext } from 'graphql-yoga'
174
+ import { useResponseCache } from '@envelop/response-cache'
175
+ import { createKvCache } from '@envelop/response-cache-cloudflare-kv'
176
+ import { resolvers } from './graphql-schema/resolvers.generated'
177
+ import { typeDefs } from './graphql-schema/typeDefs.generated'
178
+
179
+ export type Env = {
180
+ GRAPHQL_RESPONSE_CACHE: KVNamespace
181
+ }
182
+ export type GraphQLContext = YogaInitialContext & Env & ExecutionContext
183
+
184
+ export default {
185
+ async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
186
+ const cache = createKvCache({
187
+ KV: env.GRAPHQL_RESPONSE_CACHE,
188
+ ctx,
189
+ keyPrefix: 'graphql' // optional
190
+ })
191
+
192
+ const graphqlServer = createYoga<GraphQLContext>({
193
+ schema: createSchema({ typeDefs, resolvers }),
194
+ plugins: [
195
+ useResponseCache({
196
+ cache,
197
+ session: () => null
198
+ })
199
+ ]
200
+ })
201
+
202
+ return graphqlServer.fetch(request, env, ctx)
203
+ }
204
+ }
205
+ ```
206
+
207
+ > Note: In the Recipes below, be sure to provide your Cloudflare KV `cache` instance with
208
+ > `useResponseCache({ cache })`.
209
+
156
210
  ## Recipes
157
211
 
158
212
  ### Cache with maximum TTL
package/cjs/plugin.js CHANGED
@@ -40,10 +40,9 @@ function defaultGetDocumentString(executionArgs) {
40
40
  return (0, core_1.getDocumentString)(executionArgs.document, graphql_1.print);
41
41
  }
42
42
  exports.defaultGetDocumentString = defaultGetDocumentString;
43
- const originalDocumentMap = new WeakMap();
44
43
  const addEntityInfosToDocument = (0, utils_1.memoize4)(function addTypeNameToDocument(document, addTypeNameToDocumentOpts, schema, idFieldByTypeName) {
45
44
  const typeInfo = new graphql_1.TypeInfo(schema);
46
- const newDocument = (0, graphql_1.visit)(document, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
45
+ return (0, graphql_1.visit)(document, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
47
46
  OperationDefinition: {
48
47
  enter(node) {
49
48
  if (!addTypeNameToDocumentOpts.invalidateViaMutation && node.operation === 'mutation') {
@@ -85,8 +84,6 @@ const addEntityInfosToDocument = (0, utils_1.memoize4)(function addTypeNameToDoc
85
84
  };
86
85
  },
87
86
  }));
88
- originalDocumentMap.set(newDocument, document);
89
- return newDocument;
90
87
  });
91
88
  function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache)(), ttl: globalTtl = Infinity, session, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, scopePerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = exports.defaultBuildResponseCacheKey, getDocumentString = defaultGetDocumentString, shouldCacheResult = exports.defaultShouldCacheResult, includeExtensionMetadata = typeof process !== 'undefined'
92
89
  ? // eslint-disable-next-line dot-notation
@@ -94,7 +91,6 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
94
91
  : false, }) {
95
92
  const ignoredTypesMap = new Set(ignoredTypes);
96
93
  const typePerSchemaCoordinateMap = new Map();
97
- enabled = enabled ? (0, utils_1.memoize1)(enabled) : enabled;
98
94
  // never cache Introspections
99
95
  ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
100
96
  const addTypeNameToDocumentOpts = { invalidateViaMutation };
@@ -107,17 +103,6 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
107
103
  return Object.keys(data).some(fieldName => scopePerSchemaCoordinate[`${typeName}.${fieldName}`] === 'PRIVATE');
108
104
  }
109
105
  return {
110
- onParse() {
111
- return ({ result, replaceParseResult, context }) => {
112
- if (enabled && !enabled(context)) {
113
- return;
114
- }
115
- if (!originalDocumentMap.has(result) && result.kind === graphql_1.Kind.DOCUMENT) {
116
- const newDocument = addEntityInfosToDocument(result, addTypeNameToDocumentOpts, schema, idFieldByTypeName);
117
- replaceParseResult(newDocument);
118
- }
119
- };
120
- },
121
106
  onSchemaChange({ schema: newSchema }) {
122
107
  if (schema === newSchema) {
123
108
  return;
@@ -226,6 +211,10 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
226
211
  if (invalidateViaMutation !== false) {
227
212
  const operationAST = (0, graphql_1.getOperationAST)(onExecuteParams.args.document, onExecuteParams.args.operationName);
228
213
  if (operationAST?.operation === 'mutation') {
214
+ onExecuteParams.setExecuteFn(args => onExecuteParams.executeFn({
215
+ ...args,
216
+ document: addEntityInfosToDocument(args.document, addTypeNameToDocumentOpts, args.schema, idFieldByTypeName),
217
+ }));
229
218
  return {
230
219
  onExecuteDone({ result, setResult }) {
231
220
  if ((0, core_1.isAsyncIterable)(result)) {
@@ -245,12 +234,14 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
245
234
  });
246
235
  const cachedResponse = (await cache.get(cacheKey));
247
236
  if (cachedResponse != null) {
248
- if (includeExtensionMetadata) {
249
- onExecuteParams.setResultAndStopExecution(resultWithMetadata(cachedResponse, { hit: true }));
250
- }
251
- else {
252
- onExecuteParams.setResultAndStopExecution(cachedResponse);
253
- }
237
+ onExecuteParams.setExecuteFn(() => {
238
+ if (includeExtensionMetadata) {
239
+ return resultWithMetadata(cachedResponse, { hit: true });
240
+ }
241
+ else {
242
+ return cachedResponse;
243
+ }
244
+ });
254
245
  return;
255
246
  }
256
247
  if (ttlPerSchemaCoordinate) {
@@ -283,6 +274,12 @@ function useResponseCache({ cache = (0, in_memory_cache_js_1.createInMemoryCache
283
274
  setResult(resultWithMetadata(result, { hit: false, didCache: true, ttl: finalTtl }));
284
275
  }
285
276
  }
277
+ onExecuteParams.setExecuteFn(args => {
278
+ return onExecuteParams.executeFn({
279
+ ...args,
280
+ document: addEntityInfosToDocument(args.document, addTypeNameToDocumentOpts, args.schema, idFieldByTypeName),
281
+ });
282
+ });
286
283
  return {
287
284
  onExecuteDone({ result, setResult }) {
288
285
  if ((0, core_1.isAsyncIterable)(result)) {
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, memoize1, memoize4, 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
  /**
@@ -33,10 +33,9 @@ export const defaultShouldCacheResult = (params) => {
33
33
  export function defaultGetDocumentString(executionArgs) {
34
34
  return getDocumentString(executionArgs.document, print);
35
35
  }
36
- const originalDocumentMap = new WeakMap();
37
36
  const addEntityInfosToDocument = memoize4(function addTypeNameToDocument(document, addTypeNameToDocumentOpts, schema, idFieldByTypeName) {
38
37
  const typeInfo = new TypeInfo(schema);
39
- const newDocument = visit(document, visitWithTypeInfo(typeInfo, {
38
+ return visit(document, visitWithTypeInfo(typeInfo, {
40
39
  OperationDefinition: {
41
40
  enter(node) {
42
41
  if (!addTypeNameToDocumentOpts.invalidateViaMutation && node.operation === 'mutation') {
@@ -78,8 +77,6 @@ const addEntityInfosToDocument = memoize4(function addTypeNameToDocument(documen
78
77
  };
79
78
  },
80
79
  }));
81
- originalDocumentMap.set(newDocument, document);
82
- return newDocument;
83
80
  });
84
81
  export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl = Infinity, session, enabled, ignoredTypes = [], ttlPerType = {}, ttlPerSchemaCoordinate = {}, scopePerSchemaCoordinate = {}, idFields = ['id'], invalidateViaMutation = true, buildResponseCacheKey = defaultBuildResponseCacheKey, getDocumentString = defaultGetDocumentString, shouldCacheResult = defaultShouldCacheResult, includeExtensionMetadata = typeof process !== 'undefined'
85
82
  ? // eslint-disable-next-line dot-notation
@@ -87,7 +84,6 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
87
84
  : false, }) {
88
85
  const ignoredTypesMap = new Set(ignoredTypes);
89
86
  const typePerSchemaCoordinateMap = new Map();
90
- enabled = enabled ? memoize1(enabled) : enabled;
91
87
  // never cache Introspections
92
88
  ttlPerSchemaCoordinate = { 'Query.__schema': 0, ...ttlPerSchemaCoordinate };
93
89
  const addTypeNameToDocumentOpts = { invalidateViaMutation };
@@ -100,17 +96,6 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
100
96
  return Object.keys(data).some(fieldName => scopePerSchemaCoordinate[`${typeName}.${fieldName}`] === 'PRIVATE');
101
97
  }
102
98
  return {
103
- onParse() {
104
- return ({ result, replaceParseResult, context }) => {
105
- if (enabled && !enabled(context)) {
106
- return;
107
- }
108
- if (!originalDocumentMap.has(result) && result.kind === Kind.DOCUMENT) {
109
- const newDocument = addEntityInfosToDocument(result, addTypeNameToDocumentOpts, schema, idFieldByTypeName);
110
- replaceParseResult(newDocument);
111
- }
112
- };
113
- },
114
99
  onSchemaChange({ schema: newSchema }) {
115
100
  if (schema === newSchema) {
116
101
  return;
@@ -219,6 +204,10 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
219
204
  if (invalidateViaMutation !== false) {
220
205
  const operationAST = getOperationAST(onExecuteParams.args.document, onExecuteParams.args.operationName);
221
206
  if (operationAST?.operation === 'mutation') {
207
+ onExecuteParams.setExecuteFn(args => onExecuteParams.executeFn({
208
+ ...args,
209
+ document: addEntityInfosToDocument(args.document, addTypeNameToDocumentOpts, args.schema, idFieldByTypeName),
210
+ }));
222
211
  return {
223
212
  onExecuteDone({ result, setResult }) {
224
213
  if (isAsyncIterable(result)) {
@@ -238,12 +227,14 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
238
227
  });
239
228
  const cachedResponse = (await cache.get(cacheKey));
240
229
  if (cachedResponse != null) {
241
- if (includeExtensionMetadata) {
242
- onExecuteParams.setResultAndStopExecution(resultWithMetadata(cachedResponse, { hit: true }));
243
- }
244
- else {
245
- onExecuteParams.setResultAndStopExecution(cachedResponse);
246
- }
230
+ onExecuteParams.setExecuteFn(() => {
231
+ if (includeExtensionMetadata) {
232
+ return resultWithMetadata(cachedResponse, { hit: true });
233
+ }
234
+ else {
235
+ return cachedResponse;
236
+ }
237
+ });
247
238
  return;
248
239
  }
249
240
  if (ttlPerSchemaCoordinate) {
@@ -276,6 +267,12 @@ export function useResponseCache({ cache = createInMemoryCache(), ttl: globalTtl
276
267
  setResult(resultWithMetadata(result, { hit: false, didCache: true, ttl: finalTtl }));
277
268
  }
278
269
  }
270
+ onExecuteParams.setExecuteFn(args => {
271
+ return onExecuteParams.executeFn({
272
+ ...args,
273
+ document: addEntityInfosToDocument(args.document, addTypeNameToDocumentOpts, args.schema, idFieldByTypeName),
274
+ });
275
+ });
279
276
  return {
280
277
  onExecuteDone({ result, setResult }) {
281
278
  if (isAsyncIterable(result)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envelop/response-cache",
3
- "version": "6.1.0",
3
+ "version": "6.1.1-alpha-20231123130839-6a4e30b3",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "@envelop/core": "^5.0.0",