@trieb.work/nextjs-turbo-redis-cache 1.2.0 → 1.3.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 (60) hide show
  1. package/.github/workflows/ci.yml +30 -6
  2. package/.github/workflows/release.yml +6 -3
  3. package/.next/trace +11 -0
  4. package/.vscode/settings.json +10 -0
  5. package/CHANGELOG.md +61 -0
  6. package/README.md +149 -34
  7. package/dist/index.d.mts +92 -20
  8. package/dist/index.d.ts +92 -20
  9. package/dist/index.js +319 -60
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +315 -60
  12. package/dist/index.mjs.map +1 -1
  13. package/package.json +14 -9
  14. package/scripts/vitest-run-staged.cjs +1 -1
  15. package/src/CachedHandler.ts +23 -9
  16. package/src/DeduplicatedRequestHandler.ts +50 -1
  17. package/src/RedisStringsHandler.ts +330 -89
  18. package/src/SyncedMap.ts +74 -4
  19. package/src/index.ts +4 -2
  20. package/src/utils/debug.ts +30 -0
  21. package/src/utils/json.ts +26 -0
  22. package/test/integration/next-app/README.md +36 -0
  23. package/test/integration/next-app/eslint.config.mjs +16 -0
  24. package/test/integration/next-app/next.config.js +6 -0
  25. package/test/integration/next-app/package-lock.json +5833 -0
  26. package/test/integration/next-app/package.json +29 -0
  27. package/test/integration/next-app/pnpm-lock.yaml +3679 -0
  28. package/test/integration/next-app/postcss.config.mjs +5 -0
  29. package/test/integration/next-app/public/file.svg +1 -0
  30. package/test/integration/next-app/public/globe.svg +1 -0
  31. package/test/integration/next-app/public/next.svg +1 -0
  32. package/test/integration/next-app/public/vercel.svg +1 -0
  33. package/test/integration/next-app/public/window.svg +1 -0
  34. package/test/integration/next-app/src/app/api/cached-static-fetch/route.ts +18 -0
  35. package/test/integration/next-app/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +27 -0
  36. package/test/integration/next-app/src/app/api/revalidatePath/route.ts +15 -0
  37. package/test/integration/next-app/src/app/api/revalidateTag/route.ts +15 -0
  38. package/test/integration/next-app/src/app/api/revalidated-fetch/route.ts +17 -0
  39. package/test/integration/next-app/src/app/api/uncached-fetch/route.ts +15 -0
  40. package/test/integration/next-app/src/app/globals.css +26 -0
  41. package/test/integration/next-app/src/app/layout.tsx +59 -0
  42. package/test/integration/next-app/src/app/page.tsx +755 -0
  43. package/test/integration/next-app/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +19 -0
  44. package/test/integration/next-app/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +34 -0
  45. package/test/integration/next-app/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +25 -0
  46. package/test/integration/next-app/src/app/pages/no-fetch/default-page/page.tsx +55 -0
  47. package/test/integration/next-app/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +19 -0
  48. package/test/integration/next-app/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +35 -0
  49. package/test/integration/next-app/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +25 -0
  50. package/test/integration/next-app/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +19 -0
  51. package/test/integration/next-app/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +32 -0
  52. package/test/integration/next-app/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +25 -0
  53. package/test/integration/next-app/src/app/revalidation-interface.tsx +267 -0
  54. package/test/integration/next-app/tsconfig.json +27 -0
  55. package/test/integration/next-app-customized/README.md +36 -0
  56. package/test/integration/next-app-customized/customized-cache-handler.js +34 -0
  57. package/test/integration/next-app-customized/eslint.config.mjs +16 -0
  58. package/test/integration/next-app-customized/next.config.js +6 -0
  59. package/test/integration/nextjs-cache-handler.integration.test.ts +840 -0
  60. package/vite.config.ts +23 -8
package/dist/index.d.ts CHANGED
@@ -1,21 +1,51 @@
1
- import { CacheHandler, IncrementalCache, CacheHandlerValue } from 'next/dist/server/lib/incremental-cache';
2
-
3
- type GetParams = Parameters<IncrementalCache['get']>;
4
- type SetParams = Parameters<IncrementalCache['set']>;
5
- type RevalidateParams = Parameters<IncrementalCache['revalidateTag']>;
1
+ type CacheEntry = {
2
+ value: unknown;
3
+ lastModified: number;
4
+ tags: string[];
5
+ };
6
6
  type CreateRedisStringsHandlerOptions = {
7
+ /** Redis database number to use. Uses DB 0 for production, DB 1 otherwise
8
+ * @default process.env.VERCEL_ENV === 'production' ? 0 : 1
9
+ */
7
10
  database?: number;
11
+ /** Prefix added to all Redis keys
12
+ * @default process.env.VERCEL_URL || 'UNDEFINED_URL_'
13
+ */
8
14
  keyPrefix?: string;
15
+ /** Timeout in milliseconds for Redis operations
16
+ * @default 5000
17
+ */
9
18
  timeoutMs?: number;
19
+ /** Number of entries to query in one batch during full sync of shared tags hash map
20
+ * @default 250
21
+ */
10
22
  revalidateTagQuerySize?: number;
23
+ /** Key used to store shared tags hash map in Redis
24
+ * @default '__sharedTags__'
25
+ */
11
26
  sharedTagsKey?: string;
27
+ /** Average interval in milliseconds between tag map full re-syncs
28
+ * @default 3600000 (1 hour)
29
+ */
12
30
  avgResyncIntervalMs?: number;
31
+ /** Enable deduplication of Redis get requests via internal in-memory cache
32
+ * @default true
33
+ */
13
34
  redisGetDeduplication?: boolean;
35
+ /** Time in milliseconds to cache Redis get results in memory. Set this to 0 to disable in-memory caching completely
36
+ * @default 10000
37
+ */
14
38
  inMemoryCachingTime?: number;
39
+ /** Default stale age in seconds for cached items
40
+ * @default 1209600 (14 days)
41
+ */
15
42
  defaultStaleAge?: number;
43
+ /** Function to calculate expire age (redis TTL value) from stale age
44
+ * @default Production: staleAge * 2, Other: staleAge * 1.2
45
+ */
16
46
  estimateExpireAge?: (staleAge: number) => number;
17
47
  };
18
- declare class RedisStringsHandler implements CacheHandler {
48
+ declare class RedisStringsHandler {
19
49
  private client;
20
50
  private sharedTagsMap;
21
51
  private revalidatedTagsMap;
@@ -30,23 +60,65 @@ declare class RedisStringsHandler implements CacheHandler {
30
60
  private defaultStaleAge;
31
61
  private estimateExpireAge;
32
62
  constructor({ database, keyPrefix, sharedTagsKey, timeoutMs, revalidateTagQuerySize, avgResyncIntervalMs, redisGetDeduplication, inMemoryCachingTime, defaultStaleAge, estimateExpireAge, }: CreateRedisStringsHandlerOptions);
33
- resetRequestCache(...args: never[]): void;
63
+ resetRequestCache(): void;
34
64
  private assertClientIsReady;
35
- get(key: GetParams[0], ctx: GetParams[1]): Promise<(CacheHandlerValue & {
36
- lastModified: number;
37
- }) | null>;
38
- set(key: SetParams[0], data: SetParams[1] & {
39
- lastModified: number;
40
- }, ctx: SetParams[2]): Promise<void>;
41
- revalidateTag(tagOrTags: RevalidateParams[0]): Promise<void>;
65
+ get(key: string, ctx: {
66
+ kind: 'APP_ROUTE' | 'APP_PAGE';
67
+ isRoutePPREnabled: boolean;
68
+ isFallback: boolean;
69
+ } | {
70
+ kind: 'FETCH';
71
+ revalidate: number;
72
+ fetchUrl: string;
73
+ fetchIdx: number;
74
+ tags: string[];
75
+ softTags: string[];
76
+ isFallback: boolean;
77
+ }): Promise<CacheEntry | null>;
78
+ set(key: string, data: {
79
+ kind: 'APP_PAGE';
80
+ status: number;
81
+ headers: {
82
+ 'x-nextjs-stale-time': string;
83
+ 'x-next-cache-tags': string;
84
+ };
85
+ html: string;
86
+ rscData: Buffer;
87
+ segmentData: unknown;
88
+ postboned: unknown;
89
+ } | {
90
+ kind: 'APP_ROUTE';
91
+ status: number;
92
+ headers: {
93
+ 'cache-control'?: string;
94
+ 'x-nextjs-stale-time': string;
95
+ 'x-next-cache-tags': string;
96
+ };
97
+ body: Buffer;
98
+ } | {
99
+ kind: 'FETCH';
100
+ data: {
101
+ headers: Record<string, string>;
102
+ body: string;
103
+ status: number;
104
+ url: string;
105
+ };
106
+ revalidate: number | false;
107
+ }, ctx: {
108
+ revalidate: number | false;
109
+ isRoutePPREnabled: boolean;
110
+ isFallback: boolean;
111
+ tags?: string[];
112
+ }): Promise<void>;
113
+ revalidateTag(tagOrTags: string | string[], ...rest: any[]): Promise<void>;
42
114
  }
43
115
 
44
- declare class CachedHandler implements CacheHandler {
116
+ declare class CachedHandler {
45
117
  constructor(options: CreateRedisStringsHandlerOptions);
46
- get(...args: Parameters<RedisStringsHandler["get"]>): ReturnType<RedisStringsHandler["get"]>;
47
- set(...args: Parameters<RedisStringsHandler["set"]>): ReturnType<RedisStringsHandler["set"]>;
48
- revalidateTag(...args: Parameters<RedisStringsHandler["revalidateTag"]>): ReturnType<RedisStringsHandler["revalidateTag"]>;
49
- resetRequestCache(...args: Parameters<RedisStringsHandler["resetRequestCache"]>): ReturnType<RedisStringsHandler["resetRequestCache"]>;
118
+ get(...args: Parameters<RedisStringsHandler['get']>): ReturnType<RedisStringsHandler['get']>;
119
+ set(...args: Parameters<RedisStringsHandler['set']>): ReturnType<RedisStringsHandler['set']>;
120
+ revalidateTag(...args: Parameters<RedisStringsHandler['revalidateTag']>): ReturnType<RedisStringsHandler['revalidateTag']>;
121
+ resetRequestCache(...args: Parameters<RedisStringsHandler['resetRequestCache']>): ReturnType<RedisStringsHandler['resetRequestCache']>;
50
122
  }
51
123
 
52
- export { CachedHandler as default };
124
+ export { RedisStringsHandler, CachedHandler as default };