payloadcms-cloudflare-kv-plugin 1.0.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/LICENSE +21 -0
- package/README.md +484 -0
- package/dist/adapter.d.ts +8 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +682 -0
- package/dist/adapter.js.map +1 -0
- package/dist/cache.d.ts +30 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +110 -0
- package/dist/cache.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +94 -0
- package/dist/utils.js.map +1 -0
- package/package.json +108 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type {\n DatabaseAdapter,\n FindArgs,\n FindGlobalArgs,\n FindGlobalVersionsArgs,\n FindOneArgs,\n PaginatedDocs,\n QueryDraftsArgs,\n TypeWithID,\n TypeWithVersion,\n} from 'payload'\n\nimport type { CloudflareKVPluginConfig, KVNamespace } from './types.js'\n\nimport { getCacheOptions, getFromCache, invalidateByPattern, setInCache } from './cache.js'\nimport {\n debugLog,\n DEFAULT_TTL,\n generateCacheKey,\n getCollectionPattern,\n getGlobalPattern,\n shouldCacheCollection,\n} from './utils.js'\n\nexport function dbAdapterWithCache({\n baseAdapter,\n kv,\n config = { defaultCacheOptions: { ttl: DEFAULT_TTL }, kv: {} as KVNamespace },\n}: {\n baseAdapter: DatabaseAdapter\n config: CloudflareKVPluginConfig\n kv: KVNamespace\n}): DatabaseAdapter {\n return {\n ...baseAdapter,\n count: async (args) => {\n const { collection } = args\n const cache = getCacheOptions({ slug: collection, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: count ${collection}` })\n return baseAdapter.count(args)\n }\n\n const cacheKey = generateCacheKey({ slug: collection, args, config, operation: 'count' })\n const cached = await getFromCache<{ totalDocs: number }>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: count ${collection}` })\n return cached\n }\n\n const result = await baseAdapter.count(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: count ${collection}` })\n\n return result\n },\n countGlobalVersions: async (args) => {\n const { global } = args\n const cache = getCacheOptions({ slug: global, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug: global, config })) {\n debugLog({ config, message: `Cache SKIP: countGlobalVersions ${global}` })\n return baseAdapter.countGlobalVersions(args)\n }\n\n const cacheKey = generateCacheKey({\n slug: global,\n args,\n config,\n operation: 'countGlobalVersions',\n versions: true,\n })\n const cached = await getFromCache<{ totalDocs: number }>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: countGlobalVersions ${global}` })\n return cached\n }\n\n const result = await baseAdapter.countGlobalVersions(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: countGlobalVersions ${global}` })\n return result\n },\n countVersions: async (args) => {\n const { collection } = args\n const cache = getCacheOptions({ slug: collection, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: countVersions ${collection}` })\n return baseAdapter.countVersions(args)\n }\n\n const cacheKey = generateCacheKey({\n slug: collection,\n args,\n config,\n operation: 'countVersions',\n versions: true,\n })\n const cached = await getFromCache<{ totalDocs: number }>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: countVersions ${collection}` })\n return cached\n }\n\n const result = await baseAdapter.countVersions(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: countVersions ${collection}` })\n return result\n },\n create: async (args) => {\n const { collection } = args\n const result = await baseAdapter.create(args)\n const cache = getCacheOptions({ slug: collection, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: create ${collection}` })\n return result\n }\n const pattern = getCollectionPattern({ collection, config })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: create ${collection}` })\n return result\n },\n deleteMany: async (args) => {\n const { collection } = args\n const result = await baseAdapter.deleteMany(args)\n const cache = getCacheOptions({ slug: collection, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: deleteMany ${collection}` })\n return result\n }\n const pattern = getCollectionPattern({ collection, config })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: deleteMany ${collection}` })\n return result\n },\n deleteOne: async (args) => {\n const { collection } = args\n const result = await baseAdapter.deleteOne(args)\n const cache = getCacheOptions({ slug: collection, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: deleteOne ${collection}` })\n return result\n }\n const pattern = getCollectionPattern({ collection, config })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: deleteOne ${collection}` })\n return result\n },\n deleteVersions: async (args) => {\n const { collection } = args\n const result = await baseAdapter.deleteVersions(args)\n const cache = getCacheOptions({ slug: collection, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: deleteVersions ${collection}` })\n return result\n }\n const pattern = getCollectionPattern({ collection, config })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: deleteVersions ${collection}` })\n return result\n },\n find: async <T = TypeWithID>(args: FindArgs) => {\n const { collection } = args\n const cache = getCacheOptions({ slug: collection, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: find ${collection}` })\n return baseAdapter.find<T>(args)\n }\n\n const cacheKey = generateCacheKey({ slug: collection, args, config, operation: 'find' })\n const cached = await getFromCache<PaginatedDocs<T>>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: find ${collection}` })\n return cached\n }\n\n const result = await baseAdapter.find<T>(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: find ${collection}` })\n\n return result\n },\n findGlobal: async <T extends Record<string, unknown>>(args: FindGlobalArgs) => {\n const { slug } = args\n const cache = getCacheOptions({ slug, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug, config })) {\n debugLog({ config, message: `Cache SKIP: findGlobal ${slug}` })\n return baseAdapter.findGlobal<T>(args)\n }\n\n const cacheKey = generateCacheKey({ slug, args, config, operation: 'findGlobal' })\n const cached = await getFromCache<T>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: findGlobal ${slug}` })\n return cached\n }\n\n const result = await baseAdapter.findGlobal<T>(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: findGlobal ${slug}` })\n\n return result\n },\n findGlobalVersions: async <T>(args: FindGlobalVersionsArgs) => {\n const { global } = args\n const cache = getCacheOptions({ slug: global, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug: global, config })) {\n debugLog({ config, message: `Cache SKIP: findGlobalVersions ${global}` })\n return baseAdapter.findGlobalVersions<T>(args)\n }\n\n const cacheKey = generateCacheKey({\n slug: global,\n args,\n config,\n operation: 'findGlobalVersions',\n versions: true,\n })\n const cached = await getFromCache<PaginatedDocs<TypeWithVersion<T>>>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: findGlobalVersions ${global}` })\n return cached\n }\n\n const result = await baseAdapter.findGlobalVersions<T>(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: findGlobalVersions ${global}` })\n\n return result\n },\n findOne: async <T extends TypeWithID>(args: FindOneArgs) => {\n const { collection } = args\n const cache = getCacheOptions({ slug: collection, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: findOne ${collection}` })\n return baseAdapter.findOne<T>(args)\n }\n\n const cacheKey = generateCacheKey({ slug: collection, args, config, operation: 'findOne' })\n const cached = await getFromCache<T>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: findOne ${collection}` })\n return cached\n }\n\n const result = await baseAdapter.findOne<T>(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: findOne ${collection}` })\n\n return result\n },\n queryDrafts: async <T>(args: QueryDraftsArgs) => {\n const { collection } = args\n const cache = getCacheOptions({ slug: collection, args, config })\n\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: queryDrafts ${collection}` })\n return baseAdapter.queryDrafts<T>(args)\n }\n\n const cacheKey = generateCacheKey({\n slug: collection,\n args,\n config,\n operation: 'queryDrafts',\n })\n const cached = await getFromCache<PaginatedDocs<T>>({ key: cacheKey, kv })\n if (cached) {\n debugLog({ config, message: `Cache HIT: queryDrafts ${collection}` })\n return cached\n }\n\n const result = await baseAdapter.queryDrafts<T>(args)\n await setInCache({ data: result, key: cacheKey, kv, ttl: cache?.ttl ?? DEFAULT_TTL })\n debugLog({ config, message: `Cache MISS: queryDrafts ${collection}` })\n\n return result\n },\n updateGlobal: async (args) => {\n const { slug } = args\n const result = await baseAdapter.updateGlobal(args)\n const cache = getCacheOptions({ slug, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug, config })) {\n debugLog({ config, message: `Cache SKIP: updateGlobal ${slug}` })\n return result\n }\n const pattern = getGlobalPattern({ config, global: slug })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: updateGlobal ${slug}` })\n return result\n },\n updateGlobalVersion: async (args) => {\n const { global } = args\n const result = await baseAdapter.updateGlobalVersion(args)\n const cache = getCacheOptions({ slug: global, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: global, config })) {\n debugLog({ config, message: `Cache SKIP: updateGlobalVersion ${global}` })\n return result\n }\n const pattern = getGlobalPattern({ config, global: args.global, versions: true })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: updateGlobalVersion ${global}` })\n return result\n },\n updateMany: async (args) => {\n const { collection } = args\n const result = await baseAdapter.updateMany(args)\n const cache = getCacheOptions({ slug: collection, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: updateMany ${collection}` })\n return result\n }\n const pattern = getCollectionPattern({ collection: args.collection, config })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: updateMany ${collection}` })\n return result\n },\n updateOne: async (args) => {\n const { collection } = args\n const result = await baseAdapter.updateOne(args)\n const cache = getCacheOptions({ slug: collection, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: updateOne ${collection}` })\n return result\n }\n const pattern = getCollectionPattern({ collection: args.collection, config })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: updateOne ${collection}` })\n return result\n },\n upsert: async (args) => {\n const { collection } = args\n const result = await baseAdapter.upsert(args)\n const cache = getCacheOptions({ slug: collection, args, config })\n if (cache?.skip || !shouldCacheCollection({ slug: collection, config })) {\n debugLog({ config, message: `Cache SKIP: upsert ${collection}` })\n return result\n }\n const pattern = getCollectionPattern({ collection: args.collection, config })\n await invalidateByPattern({ kv, pattern })\n debugLog({ config, message: `Cache INVALIDATE: upsert ${collection}` })\n return result\n },\n }\n}\n"],"names":["getCacheOptions","getFromCache","invalidateByPattern","setInCache","debugLog","DEFAULT_TTL","generateCacheKey","getCollectionPattern","getGlobalPattern","shouldCacheCollection","dbAdapterWithCache","baseAdapter","kv","config","defaultCacheOptions","ttl","count","args","collection","cache","slug","skip","message","cacheKey","operation","cached","key","result","data","countGlobalVersions","global","versions","countVersions","create","pattern","deleteMany","deleteOne","deleteVersions","find","findGlobal","findGlobalVersions","findOne","queryDrafts","updateGlobal","updateGlobalVersion","updateMany","updateOne","upsert"],"mappings":"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"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CacheOptions, CloudflareKVPluginConfig, DBOperationArgs, KVNamespace } from './types.js';
|
|
2
|
+
export declare function getCacheOptions({ slug, args, config, }: {
|
|
3
|
+
args: DBOperationArgs;
|
|
4
|
+
config: CloudflareKVPluginConfig;
|
|
5
|
+
slug: string;
|
|
6
|
+
}): CacheOptions | undefined;
|
|
7
|
+
export declare function getFromCache<T>({ key, kv, }: {
|
|
8
|
+
key: string;
|
|
9
|
+
kv: KVNamespace;
|
|
10
|
+
}): Promise<null | T>;
|
|
11
|
+
export declare function setInCache<T>({ data, key, kv, ttl, }: {
|
|
12
|
+
data: T;
|
|
13
|
+
key: string;
|
|
14
|
+
kv: KVNamespace;
|
|
15
|
+
ttl: number;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Invalidates cache entries matching a pattern
|
|
19
|
+
* Since KV doesn't support pattern matching like Redis, we use list() with prefix
|
|
20
|
+
* and filter keys that match the pattern
|
|
21
|
+
*
|
|
22
|
+
* Note: This operation can be slow for large namespaces as it requires listing
|
|
23
|
+
* all keys with the matching prefix. Consider using more specific prefixes
|
|
24
|
+
* or implementing a tag-based invalidation strategy for better performance.
|
|
25
|
+
*/
|
|
26
|
+
export declare function invalidateByPattern({ kv, pattern, }: {
|
|
27
|
+
kv: KVNamespace;
|
|
28
|
+
pattern: string;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,wBAAwB,EACxB,eAAe,EACf,WAAW,EACZ,MAAM,YAAY,CAAA;AAInB,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,IAAI,EACJ,MAAM,GACP,EAAE;IACD,IAAI,EAAE,eAAe,CAAA;IACrB,MAAM,EAAE,wBAAwB,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,YAAY,GAAG,SAAS,CAsB3B;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,EACpC,GAAG,EACH,EAAE,GACH,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX,EAAE,EAAE,WAAW,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAoBpB;AAED,wBAAsB,UAAU,CAAC,CAAC,EAAE,EAClC,IAAI,EACJ,GAAG,EACH,EAAE,EACF,GAAG,GACJ,EAAE;IACD,IAAI,EAAE,CAAC,CAAA;IACP,GAAG,EAAE,MAAM,CAAA;IACX,EAAE,EAAE,WAAW,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhB;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,EAAE,EACF,OAAO,GACR,EAAE;IACD,EAAE,EAAE,WAAW,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4ChB"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { DEFAULT_TTL } from './utils.js';
|
|
2
|
+
export function getCacheOptions({ slug, args, config }) {
|
|
3
|
+
if (args.req?.context?.cache) {
|
|
4
|
+
return args.req.context.cache;
|
|
5
|
+
}
|
|
6
|
+
for (const [key, value] of Object.entries(config.collections ?? {})){
|
|
7
|
+
if (key === slug) {
|
|
8
|
+
if (typeof value === 'boolean') {
|
|
9
|
+
return {
|
|
10
|
+
ttl: config.defaultCacheOptions?.ttl ?? DEFAULT_TTL
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
for (const [key, value] of Object.entries(config.globals ?? {})){
|
|
17
|
+
if (key === slug) {
|
|
18
|
+
if (typeof value === 'boolean') {
|
|
19
|
+
return {
|
|
20
|
+
ttl: config.defaultCacheOptions?.ttl ?? DEFAULT_TTL
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
export async function getFromCache({ key, kv }) {
|
|
29
|
+
try {
|
|
30
|
+
const cached = await kv.get(key, {
|
|
31
|
+
type: 'text'
|
|
32
|
+
});
|
|
33
|
+
if (cached) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(cached);
|
|
36
|
+
} catch (parseErr) {
|
|
37
|
+
console.error('[CloudflareKVPlugin] Error parsing cached value for key:', key, parseErr);
|
|
38
|
+
// If JSON parsing fails, delete the corrupted cache entry
|
|
39
|
+
await kv.delete(key).catch(()=>{
|
|
40
|
+
// Ignore delete errors
|
|
41
|
+
});
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error('[CloudflareKVPlugin] Error reading from cache for key:', key, err);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function setInCache({ data, key, kv, ttl }) {
|
|
52
|
+
try {
|
|
53
|
+
await kv.put(key, JSON.stringify(data), {
|
|
54
|
+
expirationTtl: ttl
|
|
55
|
+
});
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error('[CloudflareKVPlugin] Error writing to cache: ', err);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Invalidates cache entries matching a pattern
|
|
62
|
+
* Since KV doesn't support pattern matching like Redis, we use list() with prefix
|
|
63
|
+
* and filter keys that match the pattern
|
|
64
|
+
*
|
|
65
|
+
* Note: This operation can be slow for large namespaces as it requires listing
|
|
66
|
+
* all keys with the matching prefix. Consider using more specific prefixes
|
|
67
|
+
* or implementing a tag-based invalidation strategy for better performance.
|
|
68
|
+
*/ export async function invalidateByPattern({ kv, pattern }) {
|
|
69
|
+
try {
|
|
70
|
+
// Convert Redis-style pattern (e.g., "posts:*") to KV prefix
|
|
71
|
+
// Patterns like "prefix:posts:*" become prefix "prefix:posts:"
|
|
72
|
+
const prefixMatch = pattern.match(/^(.+?):\*$/);
|
|
73
|
+
let prefix;
|
|
74
|
+
if (prefixMatch) {
|
|
75
|
+
prefix = prefixMatch[1] + ':';
|
|
76
|
+
} else if (pattern.endsWith('*')) {
|
|
77
|
+
// Handle patterns like "prefix*" (without colon)
|
|
78
|
+
prefix = pattern.replace(/\*$/, '');
|
|
79
|
+
} else {
|
|
80
|
+
// No wildcard, treat as exact prefix match
|
|
81
|
+
prefix = pattern;
|
|
82
|
+
}
|
|
83
|
+
// List all keys with the prefix (handles pagination)
|
|
84
|
+
const allKeys = [];
|
|
85
|
+
let cursor;
|
|
86
|
+
let complete = false;
|
|
87
|
+
while(!complete){
|
|
88
|
+
const result = await kv.list({
|
|
89
|
+
cursor,
|
|
90
|
+
limit: 1000,
|
|
91
|
+
prefix
|
|
92
|
+
});
|
|
93
|
+
allKeys.push(...result.keys.map((k)=>k.name));
|
|
94
|
+
complete = result.complete;
|
|
95
|
+
cursor = result.cursor;
|
|
96
|
+
}
|
|
97
|
+
// If pattern has wildcard, filter keys that match the full pattern
|
|
98
|
+
// Otherwise, all keys with the prefix match
|
|
99
|
+
const patternRegex = pattern.includes('*') ? new RegExp('^' + pattern.replace(/\*/g, '.*') + '$') : null;
|
|
100
|
+
const keysToDelete = patternRegex ? allKeys.filter((key)=>patternRegex.test(key)) : allKeys;
|
|
101
|
+
// Delete all matching keys in parallel (with error handling)
|
|
102
|
+
if (keysToDelete.length > 0) {
|
|
103
|
+
await Promise.allSettled(keysToDelete.map((key)=>kv.delete(key)));
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error('[CloudflareKVPlugin] Error invalidating cache for pattern:', pattern, err);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cache.ts"],"sourcesContent":["import type {\n CacheOptions,\n CloudflareKVPluginConfig,\n DBOperationArgs,\n KVNamespace,\n} from './types.js'\n\nimport { DEFAULT_TTL } from './utils.js'\n\nexport function getCacheOptions({\n slug,\n args,\n config,\n}: {\n args: DBOperationArgs\n config: CloudflareKVPluginConfig\n slug: string\n}): CacheOptions | undefined {\n if (args.req?.context?.cache) {\n return args.req.context.cache\n }\n for (const [key, value] of Object.entries(config.collections ?? {})) {\n if (key === slug) {\n if (typeof value === 'boolean') {\n return { ttl: config.defaultCacheOptions?.ttl ?? DEFAULT_TTL }\n }\n return value\n }\n }\n for (const [key, value] of Object.entries(config.globals ?? {})) {\n if (key === slug) {\n if (typeof value === 'boolean') {\n return { ttl: config.defaultCacheOptions?.ttl ?? DEFAULT_TTL }\n }\n return value\n }\n }\n\n return undefined\n}\n\nexport async function getFromCache<T>({\n key,\n kv,\n}: {\n key: string\n kv: KVNamespace\n}): Promise<null | T> {\n try {\n const cached = await kv.get(key, { type: 'text' })\n if (cached) {\n try {\n return JSON.parse(cached) as T\n } catch (parseErr) {\n console.error('[CloudflareKVPlugin] Error parsing cached value for key:', key, parseErr)\n // If JSON parsing fails, delete the corrupted cache entry\n await kv.delete(key).catch(() => {\n // Ignore delete errors\n })\n return null\n }\n }\n return null\n } catch (err) {\n console.error('[CloudflareKVPlugin] Error reading from cache for key:', key, err)\n return null\n }\n}\n\nexport async function setInCache<T>({\n data,\n key,\n kv,\n ttl,\n}: {\n data: T\n key: string\n kv: KVNamespace\n ttl: number\n}): Promise<void> {\n try {\n await kv.put(key, JSON.stringify(data), { expirationTtl: ttl })\n } catch (err) {\n console.error('[CloudflareKVPlugin] Error writing to cache: ', err)\n }\n}\n\n/**\n * Invalidates cache entries matching a pattern\n * Since KV doesn't support pattern matching like Redis, we use list() with prefix\n * and filter keys that match the pattern\n *\n * Note: This operation can be slow for large namespaces as it requires listing\n * all keys with the matching prefix. Consider using more specific prefixes\n * or implementing a tag-based invalidation strategy for better performance.\n */\nexport async function invalidateByPattern({\n kv,\n pattern,\n}: {\n kv: KVNamespace\n pattern: string\n}): Promise<void> {\n try {\n // Convert Redis-style pattern (e.g., \"posts:*\") to KV prefix\n // Patterns like \"prefix:posts:*\" become prefix \"prefix:posts:\"\n const prefixMatch = pattern.match(/^(.+?):\\*$/)\n let prefix: string | undefined\n\n if (prefixMatch) {\n prefix = prefixMatch[1] + ':'\n } else if (pattern.endsWith('*')) {\n // Handle patterns like \"prefix*\" (without colon)\n prefix = pattern.replace(/\\*$/, '')\n } else {\n // No wildcard, treat as exact prefix match\n prefix = pattern\n }\n\n // List all keys with the prefix (handles pagination)\n const allKeys: string[] = []\n let cursor: string | undefined\n let complete = false\n\n while (!complete) {\n const result = await kv.list({ cursor, limit: 1000, prefix })\n allKeys.push(...result.keys.map((k) => k.name))\n complete = result.complete\n cursor = result.cursor\n }\n\n // If pattern has wildcard, filter keys that match the full pattern\n // Otherwise, all keys with the prefix match\n const patternRegex = pattern.includes('*')\n ? new RegExp('^' + pattern.replace(/\\*/g, '.*') + '$')\n : null\n\n const keysToDelete = patternRegex ? allKeys.filter((key) => patternRegex.test(key)) : allKeys\n\n // Delete all matching keys in parallel (with error handling)\n if (keysToDelete.length > 0) {\n await Promise.allSettled(keysToDelete.map((key) => kv.delete(key)))\n }\n } catch (err) {\n console.error('[CloudflareKVPlugin] Error invalidating cache for pattern:', pattern, err)\n }\n}\n"],"names":["DEFAULT_TTL","getCacheOptions","slug","args","config","req","context","cache","key","value","Object","entries","collections","ttl","defaultCacheOptions","globals","undefined","getFromCache","kv","cached","get","type","JSON","parse","parseErr","console","error","delete","catch","err","setInCache","data","put","stringify","expirationTtl","invalidateByPattern","pattern","prefixMatch","match","prefix","endsWith","replace","allKeys","cursor","complete","result","list","limit","push","keys","map","k","name","patternRegex","includes","RegExp","keysToDelete","filter","test","length","Promise","allSettled"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAI1D,eAAO,MAAM,iBAAiB,iBACd,wBAAwB,cAC7B,MAAM,KAAG,MA+BjB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { dbAdapterWithCache } from './adapter.js';
|
|
2
|
+
export const cloudflareKVCache = (pluginConfig)=>(config)=>{
|
|
3
|
+
const incomingOnInit = config.onInit;
|
|
4
|
+
config.onInit = async (payload)=>{
|
|
5
|
+
// Ensure we are executing any existing onInit functions before running our own.
|
|
6
|
+
if (incomingOnInit) {
|
|
7
|
+
await incomingOnInit(payload);
|
|
8
|
+
}
|
|
9
|
+
if (!pluginConfig.kv) {
|
|
10
|
+
throw new Error('[CloudflareKVPlugin] KV namespace must be provided');
|
|
11
|
+
}
|
|
12
|
+
// Verify KV namespace is accessible by attempting to list (with limit 1)
|
|
13
|
+
try {
|
|
14
|
+
await pluginConfig.kv.list({
|
|
15
|
+
limit: 1
|
|
16
|
+
});
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error('[CloudflareKVPlugin] Failed to access KV namespace', err);
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
const baseAdapter = payload.db;
|
|
22
|
+
if (!baseAdapter) {
|
|
23
|
+
throw new Error('[CloudflareKVPlugin] No database adapter found');
|
|
24
|
+
}
|
|
25
|
+
payload.db = dbAdapterWithCache({
|
|
26
|
+
baseAdapter,
|
|
27
|
+
config: pluginConfig,
|
|
28
|
+
kv: pluginConfig.kv
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
return config;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { CloudflareKVPluginConfig } from './types.js'\n\nimport { dbAdapterWithCache } from './adapter.js'\n\nexport const cloudflareKVCache =\n\t(pluginConfig: CloudflareKVPluginConfig) =>\n\t\t(config: Config): Config => {\n\t\t\tconst incomingOnInit = config.onInit\n\n\t\t\tconfig.onInit = async (payload) => {\n\t\t\t\t// Ensure we are executing any existing onInit functions before running our own.\n\t\t\t\tif (incomingOnInit) {\n\t\t\t\t\tawait incomingOnInit(payload)\n\t\t\t\t}\n\n\t\t\t\tif (!pluginConfig.kv) {\n\t\t\t\t\tthrow new Error('[CloudflareKVPlugin] KV namespace must be provided')\n\t\t\t\t}\n\n\t\t\t\t// Verify KV namespace is accessible by attempting to list (with limit 1)\n\t\t\t\ttry {\n\t\t\t\t\tawait pluginConfig.kv.list({ limit: 1 })\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.error('[CloudflareKVPlugin] Failed to access KV namespace', err)\n\t\t\t\t\tthrow err\n\t\t\t\t}\n\n\t\t\t\tconst baseAdapter = payload.db\n\n\t\t\t\tif (!baseAdapter) {\n\t\t\t\t\tthrow new Error('[CloudflareKVPlugin] No database adapter found')\n\t\t\t\t}\n\n\t\t\t\tpayload.db = dbAdapterWithCache({ baseAdapter, config: pluginConfig, kv: pluginConfig.kv })\n\t\t\t}\n\n\t\t\treturn config\n\t\t}\n"],"names":["dbAdapterWithCache","cloudflareKVCache","pluginConfig","config","incomingOnInit","onInit","payload","kv","Error","list","limit","err","console","error","baseAdapter","db"],"mappings":"AAIA,SAASA,kBAAkB,QAAQ,eAAc;AAEjD,OAAO,MAAMC,oBACZ,CAACC,eACA,CAACC;QACA,MAAMC,iBAAiBD,OAAOE,MAAM;QAEpCF,OAAOE,MAAM,GAAG,OAAOC;YACtB,gFAAgF;YAChF,IAAIF,gBAAgB;gBACnB,MAAMA,eAAeE;YACtB;YAEA,IAAI,CAACJ,aAAaK,EAAE,EAAE;gBACrB,MAAM,IAAIC,MAAM;YACjB;YAEA,yEAAyE;YACzE,IAAI;gBACH,MAAMN,aAAaK,EAAE,CAACE,IAAI,CAAC;oBAAEC,OAAO;gBAAE;YACvC,EAAE,OAAOC,KAAK;gBACbC,QAAQC,KAAK,CAAC,sDAAsDF;gBACpE,MAAMA;YACP;YAEA,MAAMG,cAAcR,QAAQS,EAAE;YAE9B,IAAI,CAACD,aAAa;gBACjB,MAAM,IAAIN,MAAM;YACjB;YAEAF,QAAQS,EAAE,GAAGf,mBAAmB;gBAAEc;gBAAaX,QAAQD;gBAAcK,IAAIL,aAAaK,EAAE;YAAC;QAC1F;QAEA,OAAOJ;IACR,EAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { CollectionSlug, CountArgs, CountGlobalVersionArgs, FindArgs, FindGlobalArgs, FindOneArgs, GlobalSlug, QueryDraftsArgs } from 'payload';
|
|
2
|
+
export type DBOperationArgs = CountArgs | CountGlobalVersionArgs | FindArgs | FindGlobalArgs | FindOneArgs | QueryDraftsArgs;
|
|
3
|
+
export interface CollectionCacheOptions extends CacheOptions {
|
|
4
|
+
versions?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Cloudflare KV Namespace interface
|
|
8
|
+
* This matches the KVNamespace type from @cloudflare/workers-types
|
|
9
|
+
* Reference: https://developers.cloudflare.com/workers/runtime-apis/kv/
|
|
10
|
+
*/
|
|
11
|
+
export interface KVNamespace {
|
|
12
|
+
/**
|
|
13
|
+
* Deletes a key-value pair from the KV namespace
|
|
14
|
+
* @param key The key to delete
|
|
15
|
+
*/
|
|
16
|
+
delete(key: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves a value from the KV namespace
|
|
19
|
+
* @param key The key to retrieve
|
|
20
|
+
* @param options Optional type specification for the return value
|
|
21
|
+
* @returns The value as a string (or parsed based on type), or null if not found
|
|
22
|
+
*/
|
|
23
|
+
get(key: string, options?: {
|
|
24
|
+
type?: 'arrayBuffer' | 'json' | 'stream' | 'text';
|
|
25
|
+
}): Promise<null | string>;
|
|
26
|
+
/**
|
|
27
|
+
* Lists keys in the KV namespace
|
|
28
|
+
* @param options Optional parameters for listing (prefix, limit, cursor)
|
|
29
|
+
* @returns A promise resolving to an object containing keys and pagination info
|
|
30
|
+
*/
|
|
31
|
+
list(options?: {
|
|
32
|
+
cursor?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
prefix?: string;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
complete: boolean;
|
|
37
|
+
cursor?: string;
|
|
38
|
+
keys: Array<{
|
|
39
|
+
expiration?: number;
|
|
40
|
+
metadata?: unknown;
|
|
41
|
+
name: string;
|
|
42
|
+
}>;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Stores a value in the KV namespace
|
|
46
|
+
* @param key The key to store
|
|
47
|
+
* @param value The value to store (must be a string)
|
|
48
|
+
* @param options Optional expiration settings
|
|
49
|
+
* @param options.expirationTtl Time to live in seconds from now
|
|
50
|
+
* @param options.expiration Unix timestamp (in seconds) when the key should expire
|
|
51
|
+
*/
|
|
52
|
+
put(key: string, value: string, options?: {
|
|
53
|
+
expiration?: number;
|
|
54
|
+
expirationTtl?: number;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
export type CloudflareKVPluginConfig = {
|
|
58
|
+
/**
|
|
59
|
+
* List of collections to add KV caching
|
|
60
|
+
*/
|
|
61
|
+
collections?: Partial<Record<CollectionSlug, CollectionCacheOptions | true>>;
|
|
62
|
+
debug?: boolean;
|
|
63
|
+
defaultCacheOptions?: {
|
|
64
|
+
generateKey?: ({ args, operation, versions, }: {
|
|
65
|
+
args: DBOperationArgs;
|
|
66
|
+
operation: string;
|
|
67
|
+
versions: boolean;
|
|
68
|
+
}) => string;
|
|
69
|
+
keyPrefix?: string;
|
|
70
|
+
ttl?: number;
|
|
71
|
+
versions?: boolean;
|
|
72
|
+
};
|
|
73
|
+
globals?: Partial<Record<GlobalSlug, CollectionCacheOptions | true>>;
|
|
74
|
+
/**
|
|
75
|
+
* Cloudflare KV Namespace binding
|
|
76
|
+
* This should be the KV namespace instance from your Cloudflare Worker environment
|
|
77
|
+
*/
|
|
78
|
+
kv: KVNamespace;
|
|
79
|
+
};
|
|
80
|
+
export interface CacheOptions {
|
|
81
|
+
/** Custom cache key (overrides auto-generated key) */
|
|
82
|
+
key?: string;
|
|
83
|
+
/** Skip cache for this query and always hit the database */
|
|
84
|
+
skip?: boolean;
|
|
85
|
+
/** Cache tags for grouped invalidation */
|
|
86
|
+
tags?: string[];
|
|
87
|
+
/** Custom TTL (time to live) in seconds for this query */
|
|
88
|
+
ttl?: number;
|
|
89
|
+
}
|
|
90
|
+
declare module 'payload' {
|
|
91
|
+
interface RequestContext {
|
|
92
|
+
cache?: CacheOptions;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,sBAAsB,EACtB,QAAQ,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,eAAe,EAChB,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,sBAAsB,GACtB,QAAQ,GACR,cAAc,GACd,WAAW,GACX,eAAe,CAAA;AAEnB,MAAM,WAAW,sBAAuB,SAAQ,YAAY;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAElC;;;;;OAKG;IACH,GAAG,CACD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;KAAE,GAC9D,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAAA;IAEzB;;;;OAIG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAC5E,QAAQ,EAAE,OAAO,CAAA;QACjB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,KAAK,CAAC;YAAE,UAAU,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KACvE,CAAC,CAAA;IAEF;;;;;;;OAOG;IACH,GAAG,CACD,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACxD,OAAO,CAAC,IAAI,CAAC,CAAA;CACjB;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAC,CAAC,CAAA;IAC5E,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mBAAmB,CAAC,EAAE;QACpB,WAAW,CAAC,EAAE,CAAC,EACb,IAAI,EACJ,SAAS,EACT,QAAQ,GACT,EAAE;YACD,IAAI,EAAE,eAAe,CAAA;YACrB,SAAS,EAAE,MAAM,CAAA;YACjB,QAAQ,EAAE,OAAO,CAAA;SAClB,KAAK,MAAM,CAAA;QACZ,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,QAAQ,CAAC,EAAE,OAAO,CAAA;KACnB,CAAA;IACD,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,sBAAsB,GAAG,IAAI,CAAC,CAAC,CAAA;IACpE;;;OAGG;IACH,EAAE,EAAE,WAAW,CAAA;CAChB,CAAA;AAED,MAAM,WAAW,YAAY;IAC3B,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ,4DAA4D;IAC5D,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IAEf,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAiB,cAAc;QAC7B,KAAK,CAAC,EAAE,YAAY,CAAA;KACrB;CACF"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n CollectionSlug,\n CountArgs,\n CountGlobalVersionArgs,\n FindArgs,\n FindGlobalArgs,\n FindOneArgs,\n GlobalSlug,\n QueryDraftsArgs,\n} from 'payload'\n\nexport type DBOperationArgs =\n | CountArgs\n | CountGlobalVersionArgs\n | FindArgs\n | FindGlobalArgs\n | FindOneArgs\n | QueryDraftsArgs\n\nexport interface CollectionCacheOptions extends CacheOptions {\n versions?: boolean\n}\n\n/**\n * Cloudflare KV Namespace interface\n * This matches the KVNamespace type from @cloudflare/workers-types\n * Reference: https://developers.cloudflare.com/workers/runtime-apis/kv/\n */\nexport interface KVNamespace {\n /**\n * Deletes a key-value pair from the KV namespace\n * @param key The key to delete\n */\n delete(key: string): Promise<void>\n\n /**\n * Retrieves a value from the KV namespace\n * @param key The key to retrieve\n * @param options Optional type specification for the return value\n * @returns The value as a string (or parsed based on type), or null if not found\n */\n get(\n key: string,\n options?: { type?: 'arrayBuffer' | 'json' | 'stream' | 'text' },\n ): Promise<null | string>\n\n /**\n * Lists keys in the KV namespace\n * @param options Optional parameters for listing (prefix, limit, cursor)\n * @returns A promise resolving to an object containing keys and pagination info\n */\n list(options?: { cursor?: string; limit?: number; prefix?: string }): Promise<{\n complete: boolean\n cursor?: string\n keys: Array<{ expiration?: number; metadata?: unknown; name: string }>\n }>\n\n /**\n * Stores a value in the KV namespace\n * @param key The key to store\n * @param value The value to store (must be a string)\n * @param options Optional expiration settings\n * @param options.expirationTtl Time to live in seconds from now\n * @param options.expiration Unix timestamp (in seconds) when the key should expire\n */\n put(\n key: string,\n value: string,\n options?: { expiration?: number; expirationTtl?: number },\n ): Promise<void>\n}\n\nexport type CloudflareKVPluginConfig = {\n /**\n * List of collections to add KV caching\n */\n collections?: Partial<Record<CollectionSlug, CollectionCacheOptions | true>>\n debug?: boolean\n defaultCacheOptions?: {\n generateKey?: ({\n args,\n operation,\n versions,\n }: {\n args: DBOperationArgs\n operation: string\n versions: boolean\n }) => string\n keyPrefix?: string\n ttl?: number\n versions?: boolean\n }\n globals?: Partial<Record<GlobalSlug, CollectionCacheOptions | true>>\n /**\n * Cloudflare KV Namespace binding\n * This should be the KV namespace instance from your Cloudflare Worker environment\n */\n kv: KVNamespace\n}\n\nexport interface CacheOptions {\n /** Custom cache key (overrides auto-generated key) */\n key?: string\n\n /** Skip cache for this query and always hit the database */\n skip?: boolean\n\n /** Cache tags for grouped invalidation */\n tags?: string[]\n\n /** Custom TTL (time to live) in seconds for this query */\n ttl?: number\n}\n\ndeclare module 'payload' {\n export interface RequestContext {\n cache?: CacheOptions\n }\n}\n"],"names":[],"mappings":"AAoGA,WAYC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CollectionSlug, GlobalSlug } from 'payload';
|
|
2
|
+
import type { CloudflareKVPluginConfig, DBOperationArgs } from './types.js';
|
|
3
|
+
export declare const DEFAULT_TTL = 300;
|
|
4
|
+
export declare function shouldCacheCollection({ slug, config, versions, }: {
|
|
5
|
+
config: CloudflareKVPluginConfig;
|
|
6
|
+
slug: string;
|
|
7
|
+
versions?: boolean;
|
|
8
|
+
}): boolean;
|
|
9
|
+
export declare function generateCacheKey({ slug, args, config, operation, versions, }: {
|
|
10
|
+
args: DBOperationArgs;
|
|
11
|
+
config: CloudflareKVPluginConfig;
|
|
12
|
+
operation: string;
|
|
13
|
+
slug: string;
|
|
14
|
+
versions?: boolean;
|
|
15
|
+
}): string;
|
|
16
|
+
export declare function getCollectionPattern({ collection, config, versions, }: {
|
|
17
|
+
collection: CollectionSlug;
|
|
18
|
+
config: CloudflareKVPluginConfig;
|
|
19
|
+
versions?: boolean;
|
|
20
|
+
}): string;
|
|
21
|
+
export declare function getGlobalPattern({ config, global, versions, }: {
|
|
22
|
+
config: CloudflareKVPluginConfig;
|
|
23
|
+
global: GlobalSlug;
|
|
24
|
+
versions?: boolean;
|
|
25
|
+
}): string;
|
|
26
|
+
export declare function getTagPatterns({ config, tags, }: {
|
|
27
|
+
config: CloudflareKVPluginConfig;
|
|
28
|
+
tags: string[];
|
|
29
|
+
}): string[];
|
|
30
|
+
export declare function debugLog({ config, data, error, message, }: {
|
|
31
|
+
config: CloudflareKVPluginConfig;
|
|
32
|
+
data?: unknown;
|
|
33
|
+
error?: boolean;
|
|
34
|
+
message: string;
|
|
35
|
+
}): void;
|
|
36
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAIzD,OAAO,KAAK,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE3E,eAAO,MAAM,WAAW,MAAM,CAAA;AAE9B,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,MAAM,EACN,QAAgB,GACjB,EAAE;IACD,MAAM,EAAE,wBAAwB,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,WAmBA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,SAAS,EACT,QAAgB,GACjB,EAAE;IACD,IAAI,EAAE,eAAe,CAAA;IACrB,MAAM,EAAE,wBAAwB,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,UAiCA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,UAAU,EACV,MAAM,EACN,QAAgB,GACjB,EAAE;IACD,UAAU,EAAE,cAAc,CAAA;IAC1B,MAAM,EAAE,wBAAwB,CAAA;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,UAOA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,QAAgB,GACjB,EAAE;IACD,MAAM,EAAE,wBAAwB,CAAA;IAChC,MAAM,EAAE,UAAU,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,UAOA;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,IAAI,GACL,EAAE;IACD,MAAM,EAAE,wBAAwB,CAAA;IAChC,IAAI,EAAE,MAAM,EAAE,CAAA;CACf,YAMA;AAED,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,IAAI,EACJ,KAAa,EACb,OAAO,GACR,EAAE;IACD,MAAM,EAAE,wBAAwB,CAAA;IAChC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB,QAQA"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
export const DEFAULT_TTL = 300;
|
|
3
|
+
export function shouldCacheCollection({ slug, config, versions = false }) {
|
|
4
|
+
if (config.collections && Object.entries(config.collections).length > 0) {
|
|
5
|
+
if (versions) {
|
|
6
|
+
for (const [key, value] of Object.entries(config.collections)){
|
|
7
|
+
if (key === slug) {
|
|
8
|
+
if (typeof value === 'boolean') {
|
|
9
|
+
return config.defaultCacheOptions?.versions ?? false;
|
|
10
|
+
}
|
|
11
|
+
return value?.versions ?? false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return Object.keys(config.collections).includes(slug);
|
|
17
|
+
}
|
|
18
|
+
if (config.globals && Object.entries(config.globals).length > 0) {
|
|
19
|
+
return Object.keys(config.globals).includes(slug);
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
export function generateCacheKey({ slug, args, config, operation, versions = false }) {
|
|
24
|
+
const prefix = config.defaultCacheOptions?.keyPrefix;
|
|
25
|
+
const generateKey = config.defaultCacheOptions?.generateKey;
|
|
26
|
+
const key = args.req?.context?.cache?.key;
|
|
27
|
+
if (key) {
|
|
28
|
+
if (prefix) {
|
|
29
|
+
return `${prefix}:${key}`;
|
|
30
|
+
}
|
|
31
|
+
return key;
|
|
32
|
+
}
|
|
33
|
+
if (generateKey) {
|
|
34
|
+
if (prefix) {
|
|
35
|
+
return `${prefix}:${generateKey({
|
|
36
|
+
args,
|
|
37
|
+
operation,
|
|
38
|
+
versions
|
|
39
|
+
})}`;
|
|
40
|
+
}
|
|
41
|
+
return generateKey({
|
|
42
|
+
args,
|
|
43
|
+
operation,
|
|
44
|
+
versions
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const dataToHash = {
|
|
48
|
+
slug,
|
|
49
|
+
locale: args.locale,
|
|
50
|
+
operation,
|
|
51
|
+
versions,
|
|
52
|
+
where: args.where
|
|
53
|
+
};
|
|
54
|
+
const hash = createHash('md5').update(JSON.stringify(dataToHash)).digest('hex');
|
|
55
|
+
const slugKey = versions ? `${slug}:versions` : slug;
|
|
56
|
+
if (prefix) {
|
|
57
|
+
return `${prefix}:${slugKey}:${operation}:${hash}`;
|
|
58
|
+
}
|
|
59
|
+
return `${slugKey}:${operation}:${hash}`;
|
|
60
|
+
}
|
|
61
|
+
export function getCollectionPattern({ collection, config, versions = false }) {
|
|
62
|
+
const prefix = config.defaultCacheOptions?.keyPrefix;
|
|
63
|
+
const slugKey = versions ? `${collection}:versions` : collection;
|
|
64
|
+
if (prefix) {
|
|
65
|
+
return `${prefix}:${slugKey}:*`;
|
|
66
|
+
}
|
|
67
|
+
return `${slugKey}:*`;
|
|
68
|
+
}
|
|
69
|
+
export function getGlobalPattern({ config, global, versions = false }) {
|
|
70
|
+
const prefix = config.defaultCacheOptions?.keyPrefix;
|
|
71
|
+
const slugKey = versions ? `${global}:versions` : global;
|
|
72
|
+
if (prefix) {
|
|
73
|
+
return `${prefix}:${slugKey}:*`;
|
|
74
|
+
}
|
|
75
|
+
return `${slugKey}:*`;
|
|
76
|
+
}
|
|
77
|
+
export function getTagPatterns({ config, tags }) {
|
|
78
|
+
const prefix = config.defaultCacheOptions?.keyPrefix;
|
|
79
|
+
if (prefix) {
|
|
80
|
+
return tags.map((tag)=>`${prefix}:*:*:*${tag}*`);
|
|
81
|
+
}
|
|
82
|
+
return tags.map((tag)=>`${tag}*`);
|
|
83
|
+
}
|
|
84
|
+
export function debugLog({ config, data, error = false, message }) {
|
|
85
|
+
if (!config.debug) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (error) {
|
|
89
|
+
return console.error(`[CloudflareKVPlugin] ${message} `, data ?? '');
|
|
90
|
+
}
|
|
91
|
+
console.log(`[CloudflareKVPlugin] ${message} `, data ?? '');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["import type { CollectionSlug, GlobalSlug } from 'payload'\n\nimport { createHash } from 'crypto'\n\nimport type { CloudflareKVPluginConfig, DBOperationArgs } from './types.js'\n\nexport const DEFAULT_TTL = 300\n\nexport function shouldCacheCollection({\n slug,\n config,\n versions = false,\n}: {\n config: CloudflareKVPluginConfig\n slug: string\n versions?: boolean\n}) {\n if (config.collections && Object.entries(config.collections).length > 0) {\n if (versions) {\n for (const [key, value] of Object.entries(config.collections)) {\n if (key === slug) {\n if (typeof value === 'boolean') {\n return config.defaultCacheOptions?.versions ?? false\n }\n return value?.versions ?? false\n }\n }\n return false\n }\n return Object.keys(config.collections).includes(slug)\n }\n if (config.globals && Object.entries(config.globals).length > 0) {\n return Object.keys(config.globals).includes(slug)\n }\n return false\n}\n\nexport function generateCacheKey({\n slug,\n args,\n config,\n operation,\n versions = false,\n}: {\n args: DBOperationArgs\n config: CloudflareKVPluginConfig\n operation: string\n slug: string\n versions?: boolean\n}) {\n const prefix = config.defaultCacheOptions?.keyPrefix\n const generateKey = config.defaultCacheOptions?.generateKey\n const key = args.req?.context?.cache?.key\n\n if (key) {\n if (prefix) {\n return `${prefix}:${key}`\n }\n return key\n }\n if (generateKey) {\n if (prefix) {\n return `${prefix}:${generateKey({ args, operation, versions })}`\n }\n return generateKey({ args, operation, versions })\n }\n\n const dataToHash = {\n slug,\n locale: args.locale,\n operation,\n versions,\n where: args.where,\n }\n const hash = createHash('md5').update(JSON.stringify(dataToHash)).digest('hex')\n\n const slugKey = versions ? `${slug}:versions` : slug\n if (prefix) {\n return `${prefix}:${slugKey}:${operation}:${hash}`\n }\n\n return `${slugKey}:${operation}:${hash}`\n}\n\nexport function getCollectionPattern({\n collection,\n config,\n versions = false,\n}: {\n collection: CollectionSlug\n config: CloudflareKVPluginConfig\n versions?: boolean\n}) {\n const prefix = config.defaultCacheOptions?.keyPrefix\n const slugKey = versions ? `${collection}:versions` : collection\n if (prefix) {\n return `${prefix}:${slugKey}:*`\n }\n return `${slugKey}:*`\n}\n\nexport function getGlobalPattern({\n config,\n global,\n versions = false,\n}: {\n config: CloudflareKVPluginConfig\n global: GlobalSlug\n versions?: boolean\n}) {\n const prefix = config.defaultCacheOptions?.keyPrefix\n const slugKey = versions ? `${global}:versions` : global\n if (prefix) {\n return `${prefix}:${slugKey}:*`\n }\n return `${slugKey}:*`\n}\n\nexport function getTagPatterns({\n config,\n tags,\n}: {\n config: CloudflareKVPluginConfig\n tags: string[]\n}) {\n const prefix = config.defaultCacheOptions?.keyPrefix\n if (prefix) {\n return tags.map((tag) => `${prefix}:*:*:*${tag}*`)\n }\n return tags.map((tag) => `${tag}*`)\n}\n\nexport function debugLog({\n config,\n data,\n error = false,\n message,\n}: {\n config: CloudflareKVPluginConfig\n data?: unknown\n error?: boolean\n message: string\n}) {\n if (!config.debug) {\n return\n }\n if (error) {\n return console.error(`[CloudflareKVPlugin] ${message} `, data ?? '')\n }\n console.log(`[CloudflareKVPlugin] ${message} `, data ?? '')\n}\n"],"names":["createHash","DEFAULT_TTL","shouldCacheCollection","slug","config","versions","collections","Object","entries","length","key","value","defaultCacheOptions","keys","includes","globals","generateCacheKey","args","operation","prefix","keyPrefix","generateKey","req","context","cache","dataToHash","locale","where","hash","update","JSON","stringify","digest","slugKey","getCollectionPattern","collection","getGlobalPattern","global","getTagPatterns","tags","map","tag","debugLog","data","error","message","debug","console","log"],"mappings":"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"}
|