@trieb.work/nextjs-turbo-redis-cache 1.17.0 → 1.18.1

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 (300) hide show
  1. package/README.md +87 -24
  2. package/dist/index.d.mts +40 -1
  3. package/dist/index.d.ts +40 -1
  4. package/dist/index.js +291 -116
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +288 -115
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +9 -6
  9. package/.cursor/skills/testing/SKILL.md +0 -121
  10. package/.github/workflows/ci.yml +0 -309
  11. package/.github/workflows/pages.yml +0 -38
  12. package/.github/workflows/release.yml +0 -207
  13. package/.prettierrc.json +0 -6
  14. package/.vscode/settings.json +0 -10
  15. package/ARCHITECTURE.md +0 -431
  16. package/CHANGELOG.md +0 -411
  17. package/commitlint.config.cjs +0 -4
  18. package/docker/redis.conf +0 -1
  19. package/docs/index.html +0 -140
  20. package/docs/robots.txt +0 -4
  21. package/docs/sitemap.xml +0 -8
  22. package/docs/styles.css +0 -141
  23. package/eslint.config.mjs +0 -26
  24. package/hooks/commit-msg +0 -10
  25. package/hooks/pre-commit +0 -11
  26. package/playwright.config.ts +0 -18
  27. package/release.config.cjs +0 -31
  28. package/scripts/prepare.sh +0 -7
  29. package/scripts/setup-git.sh +0 -8
  30. package/scripts/setup-repo-name.sh +0 -17
  31. package/scripts/vitest-run-staged.cjs +0 -44
  32. package/src/CacheComponentsHandler.ts +0 -567
  33. package/src/CachedHandler.ts +0 -53
  34. package/src/DeduplicatedRequestHandler.ts +0 -110
  35. package/src/RedisStringsHandler.ts +0 -937
  36. package/src/SyncedMap.ts +0 -482
  37. package/src/ZodHandler.ts +0 -45
  38. package/src/index.ts +0 -16
  39. package/src/serializer.ts +0 -59
  40. package/src/utils/debug.ts +0 -30
  41. package/src/utils/json.ts +0 -49
  42. package/src/utils/prefix.ts +0 -44
  43. package/test/README.md +0 -194
  44. package/test/nextjs-test-projects/next-app-15-4-11/README.md +0 -36
  45. package/test/nextjs-test-projects/next-app-15-4-11/eslint.config.mjs +0 -16
  46. package/test/nextjs-test-projects/next-app-15-4-11/next.config.js +0 -9
  47. package/test/nextjs-test-projects/next-app-15-4-11/package-lock.json +0 -5969
  48. package/test/nextjs-test-projects/next-app-15-4-11/package.json +0 -29
  49. package/test/nextjs-test-projects/next-app-15-4-11/pnpm-lock.yaml +0 -3726
  50. package/test/nextjs-test-projects/next-app-15-4-11/postcss.config.mjs +0 -5
  51. package/test/nextjs-test-projects/next-app-15-4-11/public/file.svg +0 -1
  52. package/test/nextjs-test-projects/next-app-15-4-11/public/globe.svg +0 -1
  53. package/test/nextjs-test-projects/next-app-15-4-11/public/next.svg +0 -1
  54. package/test/nextjs-test-projects/next-app-15-4-11/public/vercel.svg +0 -1
  55. package/test/nextjs-test-projects/next-app-15-4-11/public/window.svg +0 -1
  56. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/cached-static-fetch/route.ts +0 -18
  57. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  58. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidatePath/route.ts +0 -15
  59. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidateTag/route.ts +0 -15
  60. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidated-fetch/route.ts +0 -17
  61. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/uncached-fetch/route.ts +0 -15
  62. package/test/nextjs-test-projects/next-app-15-4-11/src/app/globals.css +0 -26
  63. package/test/nextjs-test-projects/next-app-15-4-11/src/app/layout.tsx +0 -59
  64. package/test/nextjs-test-projects/next-app-15-4-11/src/app/page.tsx +0 -755
  65. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  66. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  67. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  68. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  69. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  70. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  71. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  72. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  73. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  74. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  75. package/test/nextjs-test-projects/next-app-15-4-11/src/app/revalidation-interface.tsx +0 -267
  76. package/test/nextjs-test-projects/next-app-15-4-11/tsconfig.json +0 -27
  77. package/test/nextjs-test-projects/next-app-16-0-11/README.md +0 -36
  78. package/test/nextjs-test-projects/next-app-16-0-11/eslint.config.mjs +0 -18
  79. package/test/nextjs-test-projects/next-app-16-0-11/next.config.ts +0 -10
  80. package/test/nextjs-test-projects/next-app-16-0-11/package.json +0 -28
  81. package/test/nextjs-test-projects/next-app-16-0-11/pnpm-lock.yaml +0 -4163
  82. package/test/nextjs-test-projects/next-app-16-0-11/postcss.config.mjs +0 -7
  83. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/cached-static-fetch/route.ts +0 -18
  84. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  85. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidatePath/route.ts +0 -15
  86. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidateTag/route.ts +0 -20
  87. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidated-fetch/route.ts +0 -17
  88. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/uncached-fetch/route.ts +0 -15
  89. package/test/nextjs-test-projects/next-app-16-0-11/src/app/favicon.ico +0 -0
  90. package/test/nextjs-test-projects/next-app-16-0-11/src/app/globals.css +0 -26
  91. package/test/nextjs-test-projects/next-app-16-0-11/src/app/layout.tsx +0 -59
  92. package/test/nextjs-test-projects/next-app-16-0-11/src/app/page.tsx +0 -755
  93. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  94. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  95. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  96. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  97. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  98. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  99. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  100. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  101. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  102. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  103. package/test/nextjs-test-projects/next-app-16-0-11/src/app/revalidation-interface.tsx +0 -267
  104. package/test/nextjs-test-projects/next-app-16-0-11/src/app/update-tag-test/page.tsx +0 -25
  105. package/test/nextjs-test-projects/next-app-16-0-11/tsconfig.json +0 -34
  106. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/README.md +0 -36
  107. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/cache-handler.js +0 -3
  108. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/eslint.config.mjs +0 -18
  109. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/next.config.ts +0 -13
  110. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/package.json +0 -28
  111. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/pnpm-lock.yaml +0 -4206
  112. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/postcss.config.mjs +0 -7
  113. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/file.svg +0 -1
  114. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/globe.svg +0 -1
  115. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/next.svg +0 -1
  116. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/vercel.svg +0 -1
  117. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/window.svg +0 -1
  118. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  119. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  120. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  121. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  122. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  123. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  124. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/page.tsx +0 -90
  125. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  126. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  127. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  128. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  129. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/favicon.ico +0 -0
  130. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/globals.css +0 -26
  131. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/layout.tsx +0 -57
  132. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/page.tsx +0 -755
  133. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/revalidation-interface.tsx +0 -267
  134. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/update-tag-test/page.tsx +0 -22
  135. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/tsconfig.json +0 -34
  136. package/test/nextjs-test-projects/next-app-16-2-6/README.md +0 -36
  137. package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +0 -18
  138. package/test/nextjs-test-projects/next-app-16-2-6/next.config.ts +0 -7
  139. package/test/nextjs-test-projects/next-app-16-2-6/package.json +0 -28
  140. package/test/nextjs-test-projects/next-app-16-2-6/pnpm-lock.yaml +0 -4165
  141. package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +0 -7
  142. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/cached-static-fetch/route.ts +0 -18
  143. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  144. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidatePath/route.ts +0 -15
  145. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidateTag/route.ts +0 -20
  146. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidated-fetch/route.ts +0 -17
  147. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/uncached-fetch/route.ts +0 -15
  148. package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
  149. package/test/nextjs-test-projects/next-app-16-2-6/src/app/globals.css +0 -26
  150. package/test/nextjs-test-projects/next-app-16-2-6/src/app/layout.tsx +0 -59
  151. package/test/nextjs-test-projects/next-app-16-2-6/src/app/page.tsx +0 -755
  152. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  153. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  154. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  155. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  156. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  157. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  158. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  159. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  160. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  161. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  162. package/test/nextjs-test-projects/next-app-16-2-6/src/app/revalidation-interface.tsx +0 -267
  163. package/test/nextjs-test-projects/next-app-16-2-6/src/app/update-tag-test/page.tsx +0 -25
  164. package/test/nextjs-test-projects/next-app-16-2-6/tsconfig.json +0 -34
  165. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/README.md +0 -36
  166. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +0 -3
  167. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/eslint.config.mjs +0 -18
  168. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +0 -13
  169. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/package.json +0 -28
  170. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/pnpm-lock.yaml +0 -4165
  171. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/postcss.config.mjs +0 -7
  172. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/file.svg +0 -1
  173. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/globe.svg +0 -1
  174. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/next.svg +0 -1
  175. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/vercel.svg +0 -1
  176. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/window.svg +0 -1
  177. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  178. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  179. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  180. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  181. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  182. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  183. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +0 -90
  184. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  185. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  186. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  187. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  188. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/favicon.ico +0 -0
  189. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/globals.css +0 -26
  190. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/layout.tsx +0 -57
  191. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/page.tsx +0 -755
  192. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/revalidation-interface.tsx +0 -267
  193. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +0 -22
  194. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/tsconfig.json +0 -34
  195. package/test/nextjs-test-projects/next-app-16-3-0/README.md +0 -36
  196. package/test/nextjs-test-projects/next-app-16-3-0/eslint.config.mjs +0 -18
  197. package/test/nextjs-test-projects/next-app-16-3-0/next.config.ts +0 -7
  198. package/test/nextjs-test-projects/next-app-16-3-0/package.json +0 -28
  199. package/test/nextjs-test-projects/next-app-16-3-0/pnpm-lock.yaml +0 -4284
  200. package/test/nextjs-test-projects/next-app-16-3-0/postcss.config.mjs +0 -7
  201. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/cached-static-fetch/route.ts +0 -18
  202. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  203. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidatePath/route.ts +0 -15
  204. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidateTag/route.ts +0 -20
  205. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidated-fetch/route.ts +0 -17
  206. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/uncached-fetch/route.ts +0 -15
  207. package/test/nextjs-test-projects/next-app-16-3-0/src/app/favicon.ico +0 -0
  208. package/test/nextjs-test-projects/next-app-16-3-0/src/app/globals.css +0 -26
  209. package/test/nextjs-test-projects/next-app-16-3-0/src/app/layout.tsx +0 -59
  210. package/test/nextjs-test-projects/next-app-16-3-0/src/app/page.tsx +0 -755
  211. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  212. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  213. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  214. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  215. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  216. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  217. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  218. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  219. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  220. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  221. package/test/nextjs-test-projects/next-app-16-3-0/src/app/revalidation-interface.tsx +0 -267
  222. package/test/nextjs-test-projects/next-app-16-3-0/src/app/update-tag-test/page.tsx +0 -25
  223. package/test/nextjs-test-projects/next-app-16-3-0/tsconfig.json +0 -34
  224. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/README.md +0 -36
  225. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/cache-handler.js +0 -3
  226. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/eslint.config.mjs +0 -18
  227. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/next.config.ts +0 -13
  228. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/package.json +0 -28
  229. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/pnpm-lock.yaml +0 -4284
  230. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/postcss.config.mjs +0 -7
  231. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/file.svg +0 -1
  232. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/globe.svg +0 -1
  233. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/next.svg +0 -1
  234. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/vercel.svg +0 -1
  235. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/window.svg +0 -1
  236. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  237. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  238. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  239. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  240. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  241. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  242. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/page.tsx +0 -90
  243. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  244. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  245. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  246. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  247. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/favicon.ico +0 -0
  248. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/globals.css +0 -26
  249. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/layout.tsx +0 -57
  250. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/page.tsx +0 -755
  251. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/revalidation-interface.tsx +0 -267
  252. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/update-tag-test/page.tsx +0 -22
  253. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/tsconfig.json +0 -34
  254. package/test/nextjs-test-projects/next-app-customized/README.md +0 -36
  255. package/test/nextjs-test-projects/next-app-customized/customized-cache-handler.js +0 -34
  256. package/test/nextjs-test-projects/next-app-customized/eslint.config.mjs +0 -16
  257. package/test/nextjs-test-projects/next-app-customized/next.config.js +0 -6
  258. package/test/nextjs-test-projects/next-pages-16-2-6/README.md +0 -16
  259. package/test/nextjs-test-projects/next-pages-16-2-6/eslint.config.mjs +0 -18
  260. package/test/nextjs-test-projects/next-pages-16-2-6/next.config.ts +0 -7
  261. package/test/nextjs-test-projects/next-pages-16-2-6/package.json +0 -26
  262. package/test/nextjs-test-projects/next-pages-16-2-6/pnpm-lock.yaml +0 -3896
  263. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/api/revalidate.ts +0 -24
  264. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/index.tsx +0 -11
  265. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/isr/[slug].tsx +0 -49
  266. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/static-forever.tsx +0 -20
  267. package/test/nextjs-test-projects/next-pages-16-2-6/tsconfig.json +0 -34
  268. package/test/nextjs-test-projects/next-pages-16-3-0/README.md +0 -16
  269. package/test/nextjs-test-projects/next-pages-16-3-0/eslint.config.mjs +0 -18
  270. package/test/nextjs-test-projects/next-pages-16-3-0/next.config.ts +0 -7
  271. package/test/nextjs-test-projects/next-pages-16-3-0/package.json +0 -26
  272. package/test/nextjs-test-projects/next-pages-16-3-0/pnpm-lock.yaml +0 -3939
  273. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/api/revalidate.ts +0 -24
  274. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/index.tsx +0 -11
  275. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/isr/[slug].tsx +0 -49
  276. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/static-forever.tsx +0 -20
  277. package/test/nextjs-test-projects/next-pages-16-3-0/tsconfig.json +0 -34
  278. package/test/playwright/cache-lab.spec.ts +0 -157
  279. package/test/playwright/update-tag.spec.ts +0 -33
  280. package/test/vitest/integration/build-id-prefix.integration.test.ts +0 -102
  281. package/test/vitest/integration/cache-components/cache-components.integration.test.ts +0 -231
  282. package/test/vitest/integration/cache-components/redis-kill-reconnect.test.ts +0 -65
  283. package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +0 -59
  284. package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +0 -100
  285. package/test/vitest/integration/cache-components/scripts/redis-kill-reconnect.ts +0 -243
  286. package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +0 -113
  287. package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +0 -379
  288. package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +0 -155
  289. package/test/vitest/integration/nextjs-cache-handler.integration.test.ts +0 -945
  290. package/test/vitest/integration/pages-router.integration.test.ts +0 -420
  291. package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +0 -154
  292. package/test/vitest/unit/index.test.ts +0 -173
  293. package/test/vitest/unit/pages-router-kinds.test.ts +0 -292
  294. package/test/vitest/unit/reconnect-socket-already-opened.test.ts +0 -117
  295. package/test/vitest/unit/serializer.test.ts +0 -182
  296. package/test/vitest/unit/utils/prefix.test.ts +0 -115
  297. package/tsconfig.json +0 -18
  298. package/tsup.config.ts +0 -14
  299. package/vite.config.ts +0 -34
  300. package/vitest.cache-components.config.ts +0 -16
package/README.md CHANGED
@@ -90,6 +90,8 @@ const nextConfig = {
90
90
 
91
91
  Make sure to set either REDIS_URL or REDISHOST and REDISPORT environment variables.
92
92
 
93
+ Redis connections are skipped during `next build` (`NEXT_PHASE=phase-production-build`), so a production build can succeed without Redis. The handler connects when Next.js first calls it at runtime (`next start`).
94
+
93
95
  ### Option B: create a wrapper file to change options
94
96
 
95
97
  create new file `customized-cache-handler.js` in your project root and add the following code:
@@ -131,6 +133,8 @@ module.exports = class CustomizedCacheHandler {
131
133
  }
132
134
  ```
133
135
 
136
+ `defaultStaleAge` and `estimateExpireAge` are fallbacks for when Next.js does not pass `cacheControl.expire`. On Next.js 16.3+ ISR, Redis TTL is `expire` and these options do not change it.
137
+
134
138
  extend `next.config.js` with:
135
139
 
136
140
  ```
@@ -145,23 +149,23 @@ A working example of above can be found in the `test/nextjs-test-projects/next-a
145
149
 
146
150
  ## Available Options
147
151
 
148
- | Option | Description | Default Value |
149
- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
150
- | redisUrl | Redis connection url | `process.env.REDIS_URL? process.env.REDIS_URL : process.env.REDISHOST ? redis://${process.env.REDISHOST}:${process.env.REDISPORT} : 'redis://localhost:6379'` |
151
- | database | Redis database number to use. Uses DB 0 for production, DB 1 otherwise | `process.env.VERCEL_ENV === 'production' ? 0 : 1` |
152
- | keyPrefix | Prefix added to all Redis keys | `RedisStringsHandler` default: `process.env.KEY_PREFIX \|\| process.env.VERCEL_URL \|\| 'UNDEFINED_URL_'`<br> Next handlers resolve: `options.keyPrefix \|\| KEY_PREFIX \|\| VERCEL_URL \|\| BUILD_ID \|\| 'UNDEFINED_URL_'` |
153
- | sharedTagsKey | Key used to store shared tags hash map in Redis | `'__sharedTags__'` |
154
- | getTimeoutMs | Timeout in milliseconds for time critical Redis operations. If Redis get is not fulfilled within this time, returns null to avoid blocking site rendering. | `process.env.REDIS_COMMAND_TIMEOUT_MS ? (Number.parseInt(process.env.REDIS_COMMAND_TIMEOUT_MS) ?? 500) : 500` |
155
- | revalidateTagQuerySize | Number of entries to query in one batch during full sync of shared tags hash map | `250` |
156
- | avgResyncIntervalMs | Average interval in milliseconds between tag map full re-syncs | `3600000` (1 hour) |
157
- | redisGetDeduplication | Enable deduplication of Redis get requests via internal in-memory cache. | `true` |
158
- | inMemoryCachingTime | Time in milliseconds to cache Redis get results in memory. Set this to 0 to disable in-memory caching completely. | `10000` |
159
- | defaultStaleAge | Default stale age in seconds for cached items | `1209600` (14 days) |
160
- | estimateExpireAge | Function to calculate expire age (redis TTL value) from stale age | Production: `staleAge * 2`<br> Other: `staleAge * 1.2` |
161
- | socketOptions | Redis client socket options for TLS/SSL configuration (e.g., `{ tls: true, rejectUnauthorized: false }`) | `{ connectTimeout: timeoutMs }` |
162
- | clientOptions | Additional Redis client options (e.g., username, password) | `undefined` |
163
- | killContainerOnErrorThreshold | Number of consecutive errors before the container is killed. Set to 0 to disable. | `Number.parseInt(process.env.KILL_CONTAINER_ON_ERROR_THRESHOLD) ?? 0 : 0` |
164
- | valueSerializer | Pluggable wire-format codec for Redis string values (compression, encryption, custom encoding). See [Custom value serializer](#custom-value-serializer-compression-encryption). | `jsonCacheValueSerializer` (`JSON.stringify` with built-in `Buffer` and `Map` encoding) |
152
+ | Option | Description | Default Value |
153
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
154
+ | redisUrl | Redis connection url | `process.env.REDIS_URL? process.env.REDIS_URL : process.env.REDISHOST ? redis://${process.env.REDISHOST}:${process.env.REDISPORT} : 'redis://localhost:6379'` |
155
+ | database | Redis database number to use. Uses DB 0 for production, DB 1 otherwise | `process.env.VERCEL_ENV === 'production' ? 0 : 1` |
156
+ | keyPrefix | Prefix added to all Redis keys | `RedisStringsHandler` default: `process.env.KEY_PREFIX \|\| process.env.VERCEL_URL \|\| 'UNDEFINED_URL_'`<br> Next handlers resolve: `options.keyPrefix \|\| KEY_PREFIX \|\| VERCEL_URL \|\| BUILD_ID \|\| 'UNDEFINED_URL_'` |
157
+ | sharedTagsKey | Key used to store shared tags hash map in Redis | `'__sharedTags__'` |
158
+ | getTimeoutMs | Timeout in milliseconds for time critical Redis operations. If Redis get is not fulfilled within this time, returns null to avoid blocking site rendering. | `process.env.REDIS_COMMAND_TIMEOUT_MS ? (Number.parseInt(process.env.REDIS_COMMAND_TIMEOUT_MS) ?? 500) : 500` |
159
+ | revalidateTagQuerySize | Number of entries to query in one batch during full sync of shared tags hash map | `250` |
160
+ | avgResyncIntervalMs | Average interval in milliseconds between tag map full re-syncs | `3600000` (1 hour) |
161
+ | redisGetDeduplication | Enable deduplication of Redis get requests via internal in-memory cache. | `true` |
162
+ | inMemoryCachingTime | Time in milliseconds to cache Redis get results in memory. Set this to 0 to disable in-memory caching completely. | `10000` |
163
+ | defaultStaleAge | Fallback stale age in seconds used only when Next.js does not pass a finite `cacheControl.expire` (e.g. `revalidate: false`, or older callers that only send `revalidate`). Next 16.3+ ISR typically sends `expire` (~1 year); that value is the Redis TTL and this option is ignored. | `1209600` (14 days) |
164
+ | estimateExpireAge | Fallback to compute Redis TTL from a stale/`revalidate` age when `cacheControl.expire` is absent. Not applied when Next.js provides `expire`. | Production: `staleAge * 2`<br> Other: `staleAge * 1.2` |
165
+ | socketOptions | Redis client socket options for TLS/SSL configuration (e.g., `{ tls: true, rejectUnauthorized: false }`) | `{ connectTimeout: timeoutMs }` |
166
+ | clientOptions | Additional Redis client options (e.g., username, password) | `undefined` |
167
+ | killContainerOnErrorThreshold | Number of consecutive errors before the container is killed. Set to 0 to disable. | `Number.parseInt(process.env.KILL_CONTAINER_ON_ERROR_THRESHOLD) ?? 0 : 0` |
168
+ | valueSerializer | Pluggable wire-format codec for Redis string values (compression, encryption, custom encoding). See [Custom value serializer](#custom-value-serializer-compression-encryption). | `jsonCacheValueSerializer` (`JSON.stringify` with built-in `Buffer` and `Map` encoding) |
165
169
 
166
170
  ## Custom value serializer (compression, encryption)
167
171
 
@@ -467,7 +471,50 @@ Install the package in your Next.js app:
467
471
  pnpm add @trieb.work/nextjs-turbo-redis-cache redis
468
472
  ```
469
473
 
470
- In your Next.js app, enable Cache Components and point `cacheHandlers.default` to a module that exports the handler instance:
474
+ #### Hybrid setup (ISR + Cache Components)
475
+
476
+ Next.js has **two different handler APIs**. They are not interchangeable:
477
+
478
+ | Config key | Next.js loads it as | This package export | Methods |
479
+ | ------------------------- | --------------------------------- | ------------------------------------------ | ---------------------------------------------------------- |
480
+ | `cacheHandler` (singular) | `new Handler(options)` | **default export** (`CachedHandler` class) | `get`, `set`, `revalidateTag`, `resetRequestCache` |
481
+ | `cacheHandlers` (plural) | imported object (not constructed) | **`redisCacheHandler`** | `get`, `set`, `getExpiration`, `updateTags`, `refreshTags` |
482
+
483
+ `redisCacheHandler` is not a constructor (`new redisCacheHandler()` throws). Pointing `cacheHandler` at `./cache-handler.js` (the Cache Components object) will fail at runtime. Pointing `cacheHandlers` at the default class export will not provide `getExpiration` / `updateTags`.
484
+
485
+ For a self-hosted app that needs both ISR and `'use cache'` / `'use cache: remote'`:
486
+
487
+ ```ts
488
+ // next.config.ts
489
+ import type { NextConfig } from 'next';
490
+
491
+ const nextConfig: NextConfig = {
492
+ cacheComponents: true,
493
+ cacheHandler: require.resolve('@trieb.work/nextjs-turbo-redis-cache'),
494
+ cacheHandlers: {
495
+ default: require.resolve('./cache-handler.js'),
496
+ remote: require.resolve('./cache-handler.js'),
497
+ },
498
+ cacheMaxMemorySize: 0,
499
+ };
500
+
501
+ export default nextConfig;
502
+ ```
503
+
504
+ ```js
505
+ // cache-handler.js — Cache Components only (`cacheHandlers.default` / `.remote`)
506
+ const { redisCacheHandler } = require('@trieb.work/nextjs-turbo-redis-cache');
507
+
508
+ module.exports = redisCacheHandler;
509
+ ```
510
+
511
+ `default` and `remote` may be the same `redisCacheHandler` module: `'use cache'` uses `default`, `'use cache: remote'` uses `remote`. `cacheMaxMemorySize: 0` disables Next's in-process memory cache so Redis is shared across instances.
512
+
513
+ Do not wrap ISR and Cache Components in one file unless you implement **both** interfaces (class constructed with `new`, and a separate object export). This package ships them as two exports on purpose.
514
+
515
+ #### Cache Components only
516
+
517
+ If you only need Cache Components (no ISR `cacheHandler`), enable Cache Components and point `cacheHandlers` at `redisCacheHandler`. Include `remote` if you use `'use cache: remote'`.
471
518
 
472
519
  ```ts
473
520
  // next.config.ts
@@ -477,7 +524,9 @@ const nextConfig: NextConfig = {
477
524
  cacheComponents: true,
478
525
  cacheHandlers: {
479
526
  default: require.resolve('./cache-handler.js'),
527
+ remote: require.resolve('./cache-handler.js'),
480
528
  },
529
+ cacheMaxMemorySize: 0,
481
530
  };
482
531
 
483
532
  export default nextConfig;
@@ -508,6 +557,19 @@ Optional:
508
557
  - `VERCEL_URL`: used as a key prefix for multi-tenant isolation (also useful in tests). If unset, a default prefix is used.
509
558
  - `REDIS_COMMAND_TIMEOUT_MS`: timeout (ms) for Redis commands used by the handler.
510
559
 
560
+ ### Official caching semantics (Vercel / Next.js self-hosting docs)
561
+
562
+ This package follows the semantics documented in the [Next.js self-hosting guide](https://nextjs.org/docs/app/guides/self-hosting#configuring-caching) and the official `cache-handler-redis` example:
563
+
564
+ | Topic | Behavior |
565
+ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
566
+ | **ISR Redis TTL** | Key TTL on `cacheControl.expire`, not `revalidate`. Past `revalidate` an entry is only stale (SWR); evicting at that boundary would defeat background refresh. Legacy callers that only pass `revalidate` still get `estimateExpireAge(revalidate)`. |
567
+ | **Tag sources (ISR)** | `APP_PAGE` / `APP_ROUTE` tags come from `data.headers['x-next-cache-tags']` plus `ctx.tags`. `FETCH` tags come from `ctx.tags`. |
568
+ | **Buffer / Map serialization** | `rscData` (`Buffer`) and `segmentData` (`Map`) require custom JSON encoding — use the built-in `jsonCacheValueSerializer` or wrap it. Plain `JSON.stringify` causes `segmentData.get is not a function` on RSC navigation. |
569
+ | **`updateTags(tags, durations)`** | Persists Next.js tag-manifest fields in Redis (`stale` / `expired`). No `durations` or `{ expire: 0 }` hard-expires (`expired = now`). `expire > 0` (including `'max'` ~1 year) is the SWR window: `stale = now`, `expired = now + expire * 1000`, and `get()` returns `revalidate: -1` until that deadline. |
570
+ | **`getExpiration` pattern** | Returns max tag `expired` (may be in the future). Next.js uses this for implicit/soft tags. Explicit `cacheTag()`s are checked in `get()` via `areTagsExpired` / `areTagsStale`. |
571
+ | **Build without Redis** | During `next build` (`NEXT_PHASE`), Redis connections are skipped so CI/build pipelines without Redis still succeed. |
572
+
511
573
  ### Lazy initialization
512
574
 
513
575
  The `redisCacheHandler` export is **lazily initialized** — importing the package does **not** open a Redis connection. The connection is deferred until the first method call on the handler (when Next.js invokes it). This means:
@@ -536,6 +598,8 @@ Then open the Cache Lab pages:
536
598
  - `/cache-lab/tag-invalidation`
537
599
  - `/cache-lab/stale-while-revalidate`
538
600
  - `/cache-lab/runtime-data-suspense`
601
+ - `/cache-lab/use-cache-remote`
602
+ - `/cache-lab/revalidate-durations`
539
603
 
540
604
  To run the Playwright E2E tests against a running dev server:
541
605
 
@@ -545,13 +609,12 @@ PLAYWRIGHT_BASE_URL=http://localhost:3101 pnpm test:e2e
545
609
 
546
610
  ## Some words on nextjs caching internals
547
611
 
548
- Nextjs will use different caching objects for different pages and api routes. Currently supported are kind: APP_ROUTE and APP_PAGE.
549
-
550
- app/<segment>/route.ts files will request using the APP_ROUTE kind.
551
- app/<segment>/page.tsx files will request using the APP_PAGE kind.
552
- /favicon.ico file will request using the APP_ROUTE kind.
612
+ Next.js uses different cache entry kinds. This handler supports `APP_PAGE`, `APP_ROUTE`, `FETCH`, `PAGES`, and `REDIRECT` (plus Pages Router `notFound` stored as a null value).
553
613
 
554
- Fetch requests (inside app route or page) will request using the FETCH kind.
614
+ - `app/<segment>/page.tsx` → `APP_PAGE`
615
+ - `app/<segment>/route.ts` (and `/favicon.ico`) → `APP_ROUTE`
616
+ - `fetch()` inside App Router → `FETCH`
617
+ - Pages Router `getStaticProps` → `PAGES` / `REDIRECT`
555
618
 
556
619
  For details on how these kinds are handled internally (tag maps, deduplication, value transformation), see [ARCHITECTURE.md](./ARCHITECTURE.md).
557
620
 
package/dist/index.d.mts CHANGED
@@ -221,10 +221,12 @@ declare class RedisStringsHandler {
221
221
  private estimateExpireAge;
222
222
  private killContainerOnErrorThreshold;
223
223
  private valueSerializer;
224
+ private redisConnectionDeferred;
224
225
  constructor({ redisUrl, database, keyPrefix, sharedTagsKey, getTimeoutMs, revalidateTagQuerySize, avgResyncIntervalMs, redisGetDeduplication, inMemoryCachingTime, defaultStaleAge, estimateExpireAge, killContainerOnErrorThreshold, socketOptions, clientOptions, valueSerializer, }: CreateRedisStringsHandlerOptions);
225
226
  resetRequestCache(): void;
226
227
  private clientReadyCalls;
227
228
  private assertClientIsReady;
229
+ private isClientUnavailable;
228
230
  get(key: string, ctx: GetContext): Promise<CacheEntry | null>;
229
231
  set(key: string, data: SetCacheValue, ctx: {
230
232
  isRoutePPREnabled: boolean;
@@ -253,6 +255,43 @@ declare class CachedHandler {
253
255
  declare function bufferAndMapReviver(_: string, value: any): any;
254
256
  declare function bufferAndMapReplacer(_: string, value: any): any;
255
257
 
258
+ type ResolveCacheEntryTtlContext = {
259
+ revalidate?: number | false;
260
+ cacheControl?: {
261
+ revalidate?: number | false;
262
+ expire?: number | undefined;
263
+ };
264
+ };
265
+ type ResolveCacheEntryTtlData = {
266
+ kind?: string;
267
+ revalidate?: number | false;
268
+ } | null;
269
+ type ResolveCacheEntryTtlOptions = {
270
+ estimateExpireAge: (staleAge: number) => number;
271
+ defaultStaleAge: number;
272
+ };
273
+ /**
274
+ * Resolves the Redis TTL (seconds) for an ISR / incremental-cache entry.
275
+ *
276
+ * Official Next.js semantics (cache-handler-redis example + self-hosting docs):
277
+ * - Key TTL on `cacheControl.expire`, never on `revalidate` alone.
278
+ * - Past `revalidate` an entry is only stale (SWR); evicting at that boundary
279
+ * would defeat background refresh.
280
+ * - When no finite `expire` is provided, fall back to `estimateExpireAge(revalidate)`
281
+ * for legacy Pages Router / pre-cacheLife callers that only pass `revalidate`.
282
+ * - When neither `expire` nor `revalidate` is available, return `undefined` (no TTL;
283
+ * rely on tag-based invalidation). This is intentional — not a missing
284
+ * `defaultStaleAge` fallback. `revalidate: false` is a separate branch above
285
+ * and is covered by unit + Pages Router integration tests (`/static-forever`).
286
+ */
287
+ declare function resolveCacheEntryTtlSeconds(ctx: ResolveCacheEntryTtlContext, data: ResolveCacheEntryTtlData, options: ResolveCacheEntryTtlOptions): number | undefined;
288
+
289
+ /**
290
+ * Returns true during `next build` so handlers can skip opening Redis
291
+ * connections. Matches the official Next.js cache-handler-redis example.
292
+ */
293
+ declare function shouldDeferRedisConnection(): boolean;
294
+
256
295
  interface CacheComponentsEntry {
257
296
  value: ReadableStream<Uint8Array>;
258
297
  tags: string[];
@@ -276,4 +315,4 @@ type CreateCacheComponentsHandlerOptions = CreateRedisStringsHandlerOptions & {
276
315
  declare function getRedisCacheComponentsHandler(options?: CreateCacheComponentsHandlerOptions): CacheComponentsHandler;
277
316
  declare const redisCacheHandler: CacheComponentsHandler;
278
317
 
279
- export { type CacheValueSerializer, type CreateRedisStringsHandlerOptions, RedisStringsHandler, bufferAndMapReplacer, bufferAndMapReviver, CachedHandler as default, getRedisCacheComponentsHandler, jsonCacheValueSerializer, redisCacheHandler };
318
+ export { type CacheValueSerializer, type CreateRedisStringsHandlerOptions, RedisStringsHandler, type ResolveCacheEntryTtlContext, type ResolveCacheEntryTtlData, type ResolveCacheEntryTtlOptions, bufferAndMapReplacer, bufferAndMapReviver, CachedHandler as default, getRedisCacheComponentsHandler, jsonCacheValueSerializer, redisCacheHandler, resolveCacheEntryTtlSeconds, shouldDeferRedisConnection };
package/dist/index.d.ts CHANGED
@@ -221,10 +221,12 @@ declare class RedisStringsHandler {
221
221
  private estimateExpireAge;
222
222
  private killContainerOnErrorThreshold;
223
223
  private valueSerializer;
224
+ private redisConnectionDeferred;
224
225
  constructor({ redisUrl, database, keyPrefix, sharedTagsKey, getTimeoutMs, revalidateTagQuerySize, avgResyncIntervalMs, redisGetDeduplication, inMemoryCachingTime, defaultStaleAge, estimateExpireAge, killContainerOnErrorThreshold, socketOptions, clientOptions, valueSerializer, }: CreateRedisStringsHandlerOptions);
225
226
  resetRequestCache(): void;
226
227
  private clientReadyCalls;
227
228
  private assertClientIsReady;
229
+ private isClientUnavailable;
228
230
  get(key: string, ctx: GetContext): Promise<CacheEntry | null>;
229
231
  set(key: string, data: SetCacheValue, ctx: {
230
232
  isRoutePPREnabled: boolean;
@@ -253,6 +255,43 @@ declare class CachedHandler {
253
255
  declare function bufferAndMapReviver(_: string, value: any): any;
254
256
  declare function bufferAndMapReplacer(_: string, value: any): any;
255
257
 
258
+ type ResolveCacheEntryTtlContext = {
259
+ revalidate?: number | false;
260
+ cacheControl?: {
261
+ revalidate?: number | false;
262
+ expire?: number | undefined;
263
+ };
264
+ };
265
+ type ResolveCacheEntryTtlData = {
266
+ kind?: string;
267
+ revalidate?: number | false;
268
+ } | null;
269
+ type ResolveCacheEntryTtlOptions = {
270
+ estimateExpireAge: (staleAge: number) => number;
271
+ defaultStaleAge: number;
272
+ };
273
+ /**
274
+ * Resolves the Redis TTL (seconds) for an ISR / incremental-cache entry.
275
+ *
276
+ * Official Next.js semantics (cache-handler-redis example + self-hosting docs):
277
+ * - Key TTL on `cacheControl.expire`, never on `revalidate` alone.
278
+ * - Past `revalidate` an entry is only stale (SWR); evicting at that boundary
279
+ * would defeat background refresh.
280
+ * - When no finite `expire` is provided, fall back to `estimateExpireAge(revalidate)`
281
+ * for legacy Pages Router / pre-cacheLife callers that only pass `revalidate`.
282
+ * - When neither `expire` nor `revalidate` is available, return `undefined` (no TTL;
283
+ * rely on tag-based invalidation). This is intentional — not a missing
284
+ * `defaultStaleAge` fallback. `revalidate: false` is a separate branch above
285
+ * and is covered by unit + Pages Router integration tests (`/static-forever`).
286
+ */
287
+ declare function resolveCacheEntryTtlSeconds(ctx: ResolveCacheEntryTtlContext, data: ResolveCacheEntryTtlData, options: ResolveCacheEntryTtlOptions): number | undefined;
288
+
289
+ /**
290
+ * Returns true during `next build` so handlers can skip opening Redis
291
+ * connections. Matches the official Next.js cache-handler-redis example.
292
+ */
293
+ declare function shouldDeferRedisConnection(): boolean;
294
+
256
295
  interface CacheComponentsEntry {
257
296
  value: ReadableStream<Uint8Array>;
258
297
  tags: string[];
@@ -276,4 +315,4 @@ type CreateCacheComponentsHandlerOptions = CreateRedisStringsHandlerOptions & {
276
315
  declare function getRedisCacheComponentsHandler(options?: CreateCacheComponentsHandlerOptions): CacheComponentsHandler;
277
316
  declare const redisCacheHandler: CacheComponentsHandler;
278
317
 
279
- export { type CacheValueSerializer, type CreateRedisStringsHandlerOptions, RedisStringsHandler, bufferAndMapReplacer, bufferAndMapReviver, CachedHandler as default, getRedisCacheComponentsHandler, jsonCacheValueSerializer, redisCacheHandler };
318
+ export { type CacheValueSerializer, type CreateRedisStringsHandlerOptions, RedisStringsHandler, type ResolveCacheEntryTtlContext, type ResolveCacheEntryTtlData, type ResolveCacheEntryTtlOptions, bufferAndMapReplacer, bufferAndMapReviver, CachedHandler as default, getRedisCacheComponentsHandler, jsonCacheValueSerializer, redisCacheHandler, resolveCacheEntryTtlSeconds, shouldDeferRedisConnection };