strapi-cache 1.8.8 → 1.9.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/README.md +1 -1
- package/dist/server/index.js +8 -8
- package/dist/server/index.mjs +8 -8
- package/dist/server/src/utils/key.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,7 +139,7 @@ All of these routes are protected by the policies `admin::isAuthenticatedAdmin`
|
|
|
139
139
|
- **Packages**: Uses [lru-cache](https://github.com/isaacs/node-lru-cache) for in-memory cache. Uses [ioredis](https://github.com/redis/ioredis) for Redis and [iovalkey](https://github.com/valkey-io/iovalkey) for Valkey caching.
|
|
140
140
|
- **Automatic Invalidation**: When `autoPurgeCache` is enabled (default), relevant REST cache entries are invalidated on content create, update, or delete. When `autoPurgeGraphQL` is enabled, GraphQL cache is invalidated the same way (it is off unless you set it in config).
|
|
141
141
|
- **`no-cache` Header Support**: Respects the `no-cache` header, letting you skip the cache by setting `Cache-Control: no-cache` in your request.
|
|
142
|
-
- **Default Cached Requests**: By default, caches all GET requests to `/api` (or whatever prefix you defined) and POST requests to `/graphql
|
|
142
|
+
- **Default Cached Requests**: By default, caches all GET requests to `/api` (or whatever prefix you defined) and POST requests to `/graphql` or predefined graphql route from graphql plugin config. You can customize which routes or entities to cache using `cacheableRoutes` or `cacheableEntities` config options.
|
|
143
143
|
|
|
144
144
|
## 🔮 Planned Features
|
|
145
145
|
|
package/dist/server/index.js
CHANGED
|
@@ -63,7 +63,7 @@ async function invalidateGraphqlCache(event, cacheStore, strapi2) {
|
|
|
63
63
|
const contentType = strapi2.contentType(model.uid);
|
|
64
64
|
if (!contentType || !contentType.info) {
|
|
65
65
|
loggy.info(`Content type ${model.uid} not found, purging all GraphQL cache`);
|
|
66
|
-
const graphqlRegex2 = new RegExp(`^(GET|POST)
|
|
66
|
+
const graphqlRegex2 = new RegExp(`^(GET|POST):${strapi2.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:.*`);
|
|
67
67
|
await cacheStore.clearByRegexp([graphqlRegex2]);
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
@@ -72,12 +72,12 @@ async function invalidateGraphqlCache(event, cacheStore, strapi2) {
|
|
|
72
72
|
const fieldNames = [...new Set([singularName, pluralName].filter(Boolean))];
|
|
73
73
|
if (fieldNames.length === 0) {
|
|
74
74
|
loggy.info(`No field names for ${model.uid}, purging all GraphQL cache`);
|
|
75
|
-
const graphqlRegex2 = new RegExp(`^(GET|POST)
|
|
75
|
+
const graphqlRegex2 = new RegExp(`^(GET|POST):${strapi2.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:.*`);
|
|
76
76
|
await cacheStore.clearByRegexp([graphqlRegex2]);
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
79
|
const escapedNames = fieldNames.map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
80
|
-
const graphqlRegex = new RegExp(`^(GET|POST)
|
|
80
|
+
const graphqlRegex = new RegExp(`^(GET|POST):${strapi2.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:[^:]*\\b(${escapedNames})\\b[^:]*:`);
|
|
81
81
|
await cacheStore.clearByRegexp([graphqlRegex]);
|
|
82
82
|
loggy.info(`Invalidated GraphQL cache for ${model.uid} (${fieldNames.join(", ")})`);
|
|
83
83
|
} catch (error) {
|
|
@@ -157,10 +157,10 @@ const generateCacheKey = (context) => {
|
|
|
157
157
|
const { method } = context.request;
|
|
158
158
|
return `${method}:${url}`;
|
|
159
159
|
};
|
|
160
|
-
const generateGraphqlCacheKey = (payload, method = "POST", rootFields = []) => {
|
|
160
|
+
const generateGraphqlCacheKey = (payload, method = "POST", rootFields = [], strapi2) => {
|
|
161
161
|
const hash = crypto.createHash("sha256").update(payload).digest("base64url");
|
|
162
162
|
const rootFieldsSegment = rootFields.length > 0 ? [...rootFields].sort((a, b) => a.localeCompare(b)).join(",") : "_";
|
|
163
|
-
return `${method}
|
|
163
|
+
return `${method}:${strapi2?.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:${rootFieldsSegment}:${hash}`;
|
|
164
164
|
};
|
|
165
165
|
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
166
166
|
const generateEntityKey = (url, restApiPrefix) => {
|
|
@@ -390,7 +390,7 @@ function getRootFieldsFromQuery(query) {
|
|
|
390
390
|
}
|
|
391
391
|
const middleware = async (ctx, next) => {
|
|
392
392
|
const { url, method } = ctx.request;
|
|
393
|
-
if (!url.startsWith("/graphql")) {
|
|
393
|
+
if (!url.startsWith(strapi.plugin("graphql")?.config("endpoint", "/graphql"))) {
|
|
394
394
|
await next();
|
|
395
395
|
return;
|
|
396
396
|
}
|
|
@@ -424,7 +424,7 @@ const middleware = async (ctx, next) => {
|
|
|
424
424
|
}
|
|
425
425
|
const payload = parseGraphqlPayload(body, isGet);
|
|
426
426
|
const rootFields = getRootFieldsFromQuery(payload.query);
|
|
427
|
-
const key = generateGraphqlCacheKey(body, isGet ? "GET" : "POST", rootFields);
|
|
427
|
+
const key = generateGraphqlCacheKey(body, isGet ? "GET" : "POST", rootFields, strapi);
|
|
428
428
|
loggy.info(
|
|
429
429
|
`GraphQL request: ${JSON.stringify({
|
|
430
430
|
operationName: payload.operationName,
|
|
@@ -478,7 +478,7 @@ const middleware = async (ctx, next) => {
|
|
|
478
478
|
return;
|
|
479
479
|
}
|
|
480
480
|
await next();
|
|
481
|
-
const shouldCache = (ctx.method === "POST" || ctx.method === "GET") && ctx.status >= 200 && ctx.status < 300 && url.startsWith("/graphql");
|
|
481
|
+
const shouldCache = (ctx.method === "POST" || ctx.method === "GET") && ctx.status >= 200 && ctx.status < 300 && url.startsWith(strapi.plugin("graphql")?.config("endpoint", "/graphql"));
|
|
482
482
|
if (shouldCache) {
|
|
483
483
|
loggy.info(`MISS with key: ${key}`);
|
|
484
484
|
const headers = ctx.request.headers;
|
package/dist/server/index.mjs
CHANGED
|
@@ -59,7 +59,7 @@ async function invalidateGraphqlCache(event, cacheStore, strapi2) {
|
|
|
59
59
|
const contentType = strapi2.contentType(model.uid);
|
|
60
60
|
if (!contentType || !contentType.info) {
|
|
61
61
|
loggy.info(`Content type ${model.uid} not found, purging all GraphQL cache`);
|
|
62
|
-
const graphqlRegex2 = new RegExp(`^(GET|POST)
|
|
62
|
+
const graphqlRegex2 = new RegExp(`^(GET|POST):${strapi2.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:.*`);
|
|
63
63
|
await cacheStore.clearByRegexp([graphqlRegex2]);
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
@@ -68,12 +68,12 @@ async function invalidateGraphqlCache(event, cacheStore, strapi2) {
|
|
|
68
68
|
const fieldNames = [...new Set([singularName, pluralName].filter(Boolean))];
|
|
69
69
|
if (fieldNames.length === 0) {
|
|
70
70
|
loggy.info(`No field names for ${model.uid}, purging all GraphQL cache`);
|
|
71
|
-
const graphqlRegex2 = new RegExp(`^(GET|POST)
|
|
71
|
+
const graphqlRegex2 = new RegExp(`^(GET|POST):${strapi2.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:.*`);
|
|
72
72
|
await cacheStore.clearByRegexp([graphqlRegex2]);
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
const escapedNames = fieldNames.map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
76
|
-
const graphqlRegex = new RegExp(`^(GET|POST)
|
|
76
|
+
const graphqlRegex = new RegExp(`^(GET|POST):${strapi2.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:[^:]*\\b(${escapedNames})\\b[^:]*:`);
|
|
77
77
|
await cacheStore.clearByRegexp([graphqlRegex]);
|
|
78
78
|
loggy.info(`Invalidated GraphQL cache for ${model.uid} (${fieldNames.join(", ")})`);
|
|
79
79
|
} catch (error) {
|
|
@@ -153,10 +153,10 @@ const generateCacheKey = (context) => {
|
|
|
153
153
|
const { method } = context.request;
|
|
154
154
|
return `${method}:${url}`;
|
|
155
155
|
};
|
|
156
|
-
const generateGraphqlCacheKey = (payload, method = "POST", rootFields = []) => {
|
|
156
|
+
const generateGraphqlCacheKey = (payload, method = "POST", rootFields = [], strapi2) => {
|
|
157
157
|
const hash = createHash("sha256").update(payload).digest("base64url");
|
|
158
158
|
const rootFieldsSegment = rootFields.length > 0 ? [...rootFields].sort((a, b) => a.localeCompare(b)).join(",") : "_";
|
|
159
|
-
return `${method}
|
|
159
|
+
return `${method}:${strapi2?.plugin("graphql")?.config("endpoint", "/graphql") ?? "/graphql"}:${rootFieldsSegment}:${hash}`;
|
|
160
160
|
};
|
|
161
161
|
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
162
162
|
const generateEntityKey = (url, restApiPrefix) => {
|
|
@@ -386,7 +386,7 @@ function getRootFieldsFromQuery(query) {
|
|
|
386
386
|
}
|
|
387
387
|
const middleware = async (ctx, next) => {
|
|
388
388
|
const { url, method } = ctx.request;
|
|
389
|
-
if (!url.startsWith("/graphql")) {
|
|
389
|
+
if (!url.startsWith(strapi.plugin("graphql")?.config("endpoint", "/graphql"))) {
|
|
390
390
|
await next();
|
|
391
391
|
return;
|
|
392
392
|
}
|
|
@@ -420,7 +420,7 @@ const middleware = async (ctx, next) => {
|
|
|
420
420
|
}
|
|
421
421
|
const payload = parseGraphqlPayload(body, isGet);
|
|
422
422
|
const rootFields = getRootFieldsFromQuery(payload.query);
|
|
423
|
-
const key = generateGraphqlCacheKey(body, isGet ? "GET" : "POST", rootFields);
|
|
423
|
+
const key = generateGraphqlCacheKey(body, isGet ? "GET" : "POST", rootFields, strapi);
|
|
424
424
|
loggy.info(
|
|
425
425
|
`GraphQL request: ${JSON.stringify({
|
|
426
426
|
operationName: payload.operationName,
|
|
@@ -474,7 +474,7 @@ const middleware = async (ctx, next) => {
|
|
|
474
474
|
return;
|
|
475
475
|
}
|
|
476
476
|
await next();
|
|
477
|
-
const shouldCache = (ctx.method === "POST" || ctx.method === "GET") && ctx.status >= 200 && ctx.status < 300 && url.startsWith("/graphql");
|
|
477
|
+
const shouldCache = (ctx.method === "POST" || ctx.method === "GET") && ctx.status >= 200 && ctx.status < 300 && url.startsWith(strapi.plugin("graphql")?.config("endpoint", "/graphql"));
|
|
478
478
|
if (shouldCache) {
|
|
479
479
|
loggy.info(`MISS with key: ${key}`);
|
|
480
480
|
const headers = ctx.request.headers;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { Core } from '@strapi/strapi';
|
|
1
2
|
import { Context } from 'koa';
|
|
2
3
|
export declare const generateCacheKey: (context: Context) => string;
|
|
3
|
-
export declare const generateGraphqlCacheKey: (payload: string, method?: 'GET' | 'POST', rootFields?: string[]) => string;
|
|
4
|
+
export declare const generateGraphqlCacheKey: (payload: string, method?: 'GET' | 'POST', rootFields?: string[], strapi?: Core.Strapi) => string;
|
|
4
5
|
export declare const escapeRegExp: (s: string) => string;
|
|
5
6
|
export declare const generateEntityKey: (url: string, restApiPrefix: string) => string;
|
package/package.json
CHANGED