@trieb.work/nextjs-turbo-redis-cache 1.18.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 (318) hide show
  1. package/dist/index.js +27 -20
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +27 -20
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +6 -1
  6. package/.cursor/skills/testing/SKILL.md +0 -121
  7. package/.github/workflows/ci.yml +0 -309
  8. package/.github/workflows/pages.yml +0 -38
  9. package/.github/workflows/release.yml +0 -58
  10. package/.prettierrc.json +0 -6
  11. package/.vscode/settings.json +0 -10
  12. package/ARCHITECTURE.md +0 -470
  13. package/commitlint.config.cjs +0 -4
  14. package/docker/redis.conf +0 -1
  15. package/docs/index.html +0 -140
  16. package/docs/robots.txt +0 -4
  17. package/docs/sitemap.xml +0 -8
  18. package/docs/styles.css +0 -141
  19. package/eslint.config.mjs +0 -26
  20. package/hooks/commit-msg +0 -10
  21. package/hooks/pre-commit +0 -11
  22. package/playwright.config.ts +0 -18
  23. package/release.config.cjs +0 -12
  24. package/scripts/prepare.sh +0 -7
  25. package/scripts/setup-git.sh +0 -8
  26. package/scripts/setup-repo-name.sh +0 -17
  27. package/scripts/vitest-run-staged.cjs +0 -44
  28. package/src/CacheComponentsHandler.ts +0 -597
  29. package/src/CachedHandler.ts +0 -53
  30. package/src/DeduplicatedRequestHandler.ts +0 -110
  31. package/src/RedisStringsHandler.ts +0 -949
  32. package/src/SyncedMap.ts +0 -496
  33. package/src/ZodHandler.ts +0 -45
  34. package/src/index.ts +0 -23
  35. package/src/serializer.ts +0 -59
  36. package/src/utils/cacheTtl.ts +0 -71
  37. package/src/utils/compareAndUnlink.ts +0 -29
  38. package/src/utils/debug.ts +0 -30
  39. package/src/utils/json.ts +0 -49
  40. package/src/utils/prefix.ts +0 -44
  41. package/src/utils/redisConnection.ts +0 -17
  42. package/src/utils/tagRevalidation.ts +0 -162
  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 -15
  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/expire-matrix/revalidate/route.ts +0 -34
  122. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  123. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  124. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  125. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  126. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/page.tsx +0 -112
  127. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  128. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  129. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  130. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  131. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  132. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  133. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/favicon.ico +0 -0
  134. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/globals.css +0 -26
  135. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/layout.tsx +0 -57
  136. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/page.tsx +0 -755
  137. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/revalidation-interface.tsx +0 -267
  138. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/update-tag-test/page.tsx +0 -22
  139. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/tsconfig.json +0 -34
  140. package/test/nextjs-test-projects/next-app-16-2-6/README.md +0 -36
  141. package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +0 -18
  142. package/test/nextjs-test-projects/next-app-16-2-6/next.config.ts +0 -7
  143. package/test/nextjs-test-projects/next-app-16-2-6/package.json +0 -28
  144. package/test/nextjs-test-projects/next-app-16-2-6/pnpm-lock.yaml +0 -4165
  145. package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +0 -7
  146. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/cached-static-fetch/route.ts +0 -18
  147. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  148. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidatePath/route.ts +0 -15
  149. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidateTag/route.ts +0 -20
  150. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidated-fetch/route.ts +0 -17
  151. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/uncached-fetch/route.ts +0 -15
  152. package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
  153. package/test/nextjs-test-projects/next-app-16-2-6/src/app/globals.css +0 -26
  154. package/test/nextjs-test-projects/next-app-16-2-6/src/app/layout.tsx +0 -59
  155. package/test/nextjs-test-projects/next-app-16-2-6/src/app/page.tsx +0 -755
  156. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  157. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  158. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  159. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  160. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  161. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  162. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  163. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  164. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  165. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  166. package/test/nextjs-test-projects/next-app-16-2-6/src/app/revalidation-interface.tsx +0 -267
  167. package/test/nextjs-test-projects/next-app-16-2-6/src/app/update-tag-test/page.tsx +0 -25
  168. package/test/nextjs-test-projects/next-app-16-2-6/tsconfig.json +0 -34
  169. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/README.md +0 -36
  170. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +0 -3
  171. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/eslint.config.mjs +0 -18
  172. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +0 -15
  173. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/package.json +0 -28
  174. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/pnpm-lock.yaml +0 -4165
  175. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/postcss.config.mjs +0 -7
  176. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/file.svg +0 -1
  177. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/globe.svg +0 -1
  178. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/next.svg +0 -1
  179. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/vercel.svg +0 -1
  180. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/window.svg +0 -1
  181. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  182. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  183. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  184. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  185. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  186. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  187. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  188. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  189. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +0 -112
  190. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  191. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  192. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  193. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  194. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  195. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  196. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/favicon.ico +0 -0
  197. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/globals.css +0 -26
  198. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/layout.tsx +0 -57
  199. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/page.tsx +0 -755
  200. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/revalidation-interface.tsx +0 -267
  201. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +0 -22
  202. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/tsconfig.json +0 -34
  203. package/test/nextjs-test-projects/next-app-16-3-0/README.md +0 -36
  204. package/test/nextjs-test-projects/next-app-16-3-0/eslint.config.mjs +0 -18
  205. package/test/nextjs-test-projects/next-app-16-3-0/next.config.ts +0 -7
  206. package/test/nextjs-test-projects/next-app-16-3-0/package.json +0 -28
  207. package/test/nextjs-test-projects/next-app-16-3-0/pnpm-lock.yaml +0 -4284
  208. package/test/nextjs-test-projects/next-app-16-3-0/postcss.config.mjs +0 -7
  209. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/cached-static-fetch/route.ts +0 -18
  210. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  211. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidatePath/route.ts +0 -15
  212. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidateTag/route.ts +0 -20
  213. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidated-fetch/route.ts +0 -17
  214. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/uncached-fetch/route.ts +0 -15
  215. package/test/nextjs-test-projects/next-app-16-3-0/src/app/favicon.ico +0 -0
  216. package/test/nextjs-test-projects/next-app-16-3-0/src/app/globals.css +0 -26
  217. package/test/nextjs-test-projects/next-app-16-3-0/src/app/layout.tsx +0 -59
  218. package/test/nextjs-test-projects/next-app-16-3-0/src/app/page.tsx +0 -755
  219. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  220. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  221. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  222. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  223. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  224. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  225. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  226. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  227. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  228. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  229. package/test/nextjs-test-projects/next-app-16-3-0/src/app/revalidation-interface.tsx +0 -267
  230. package/test/nextjs-test-projects/next-app-16-3-0/src/app/update-tag-test/page.tsx +0 -25
  231. package/test/nextjs-test-projects/next-app-16-3-0/tsconfig.json +0 -34
  232. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/README.md +0 -36
  233. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/cache-handler.js +0 -3
  234. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/eslint.config.mjs +0 -18
  235. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/next.config.ts +0 -15
  236. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/package.json +0 -28
  237. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/pnpm-lock.yaml +0 -4284
  238. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/postcss.config.mjs +0 -7
  239. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/file.svg +0 -1
  240. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/globe.svg +0 -1
  241. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/next.svg +0 -1
  242. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/vercel.svg +0 -1
  243. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/window.svg +0 -1
  244. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  245. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  246. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  247. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  248. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  249. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  250. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  251. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  252. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/page.tsx +0 -112
  253. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  254. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  255. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  256. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  257. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  258. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  259. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/favicon.ico +0 -0
  260. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/globals.css +0 -26
  261. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/layout.tsx +0 -57
  262. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/page.tsx +0 -755
  263. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/revalidation-interface.tsx +0 -267
  264. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/update-tag-test/page.tsx +0 -22
  265. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/tsconfig.json +0 -34
  266. package/test/nextjs-test-projects/next-app-customized/README.md +0 -36
  267. package/test/nextjs-test-projects/next-app-customized/customized-cache-handler.js +0 -34
  268. package/test/nextjs-test-projects/next-app-customized/eslint.config.mjs +0 -16
  269. package/test/nextjs-test-projects/next-app-customized/next.config.js +0 -6
  270. package/test/nextjs-test-projects/next-pages-16-2-6/README.md +0 -16
  271. package/test/nextjs-test-projects/next-pages-16-2-6/eslint.config.mjs +0 -18
  272. package/test/nextjs-test-projects/next-pages-16-2-6/next.config.ts +0 -7
  273. package/test/nextjs-test-projects/next-pages-16-2-6/package.json +0 -26
  274. package/test/nextjs-test-projects/next-pages-16-2-6/pnpm-lock.yaml +0 -3896
  275. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/api/revalidate.ts +0 -24
  276. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/index.tsx +0 -11
  277. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/isr/[slug].tsx +0 -49
  278. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/static-forever.tsx +0 -20
  279. package/test/nextjs-test-projects/next-pages-16-2-6/tsconfig.json +0 -34
  280. package/test/nextjs-test-projects/next-pages-16-3-0/README.md +0 -16
  281. package/test/nextjs-test-projects/next-pages-16-3-0/eslint.config.mjs +0 -18
  282. package/test/nextjs-test-projects/next-pages-16-3-0/next.config.ts +0 -7
  283. package/test/nextjs-test-projects/next-pages-16-3-0/package.json +0 -26
  284. package/test/nextjs-test-projects/next-pages-16-3-0/pnpm-lock.yaml +0 -3939
  285. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/api/revalidate.ts +0 -24
  286. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/index.tsx +0 -11
  287. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/isr/[slug].tsx +0 -49
  288. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/static-forever.tsx +0 -20
  289. package/test/nextjs-test-projects/next-pages-16-3-0/tsconfig.json +0 -34
  290. package/test/playwright/cache-lab.spec.ts +0 -232
  291. package/test/playwright/update-tag.spec.ts +0 -33
  292. package/test/vitest/integration/build-id-prefix.integration.test.ts +0 -102
  293. package/test/vitest/integration/cache-components/cache-components.integration.test.ts +0 -389
  294. package/test/vitest/integration/cache-components/redis-kill-reconnect.test.ts +0 -65
  295. package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +0 -59
  296. package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +0 -100
  297. package/test/vitest/integration/cache-components/scripts/redis-kill-reconnect.ts +0 -243
  298. package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +0 -113
  299. package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +0 -379
  300. package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +0 -155
  301. package/test/vitest/integration/nextjs-cache-handler.integration.test.ts +0 -959
  302. package/test/vitest/integration/pages-router.integration.test.ts +0 -430
  303. package/test/vitest/unit/SyncedMap-orphan-cleanup.test.ts +0 -111
  304. package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +0 -154
  305. package/test/vitest/unit/cache-components-update-tags.test.ts +0 -350
  306. package/test/vitest/unit/cache-ttl.test.ts +0 -67
  307. package/test/vitest/unit/compare-and-unlink.test.ts +0 -22
  308. package/test/vitest/unit/index.test.ts +0 -190
  309. package/test/vitest/unit/pages-router-kinds.test.ts +0 -303
  310. package/test/vitest/unit/reconnect-socket-already-opened.test.ts +0 -117
  311. package/test/vitest/unit/redis-connection.test.ts +0 -39
  312. package/test/vitest/unit/serializer.test.ts +0 -182
  313. package/test/vitest/unit/tag-revalidation.test.ts +0 -178
  314. package/test/vitest/unit/utils/prefix.test.ts +0 -115
  315. package/tsconfig.json +0 -18
  316. package/tsup.config.ts +0 -14
  317. package/vite.config.ts +0 -34
  318. package/vitest.cache-components.config.ts +0 -16
@@ -1,597 +0,0 @@
1
- import { commandOptions, createClient } from 'redis';
2
- import type { RedisClientOptions } from 'redis';
3
- import {
4
- Client,
5
- CreateRedisStringsHandlerOptions,
6
- redisErrorHandler,
7
- } from './RedisStringsHandler';
8
- import { SyncedMap } from './SyncedMap';
9
- import { DeduplicatedRequestHandler } from './DeduplicatedRequestHandler';
10
- import { debug } from './utils/debug';
11
- import { resolveKeyPrefix } from './utils/prefix';
12
- import { shouldDeferRedisConnection } from './utils/redisConnection';
13
- import { compareAndUnlink } from './utils/compareAndUnlink';
14
- import {
15
- applyTagUpdate,
16
- areTagsExpired,
17
- areTagsStale,
18
- maxExpiredTimestamp,
19
- normalizeTagManifest,
20
- persistableTagManifest,
21
- type TagManifestEntry,
22
- } from './utils/tagRevalidation';
23
-
24
- export interface CacheComponentsEntry {
25
- value: ReadableStream<Uint8Array>;
26
- tags: string[];
27
- stale: number;
28
- timestamp: number;
29
- expire: number;
30
- revalidate: number;
31
- }
32
-
33
- export interface CacheComponentsHandler {
34
- get(
35
- cacheKey: string,
36
- softTags: string[],
37
- ): Promise<CacheComponentsEntry | undefined>;
38
- set(
39
- cacheKey: string,
40
- pendingEntry: Promise<CacheComponentsEntry>,
41
- ): Promise<void>;
42
- refreshTags(): Promise<void>;
43
- getExpiration(tags: string[]): Promise<number>;
44
- updateTags(tags: string[], durations?: { expire?: number }): Promise<void>;
45
- }
46
-
47
- type StoredCacheEntry = Omit<CacheComponentsEntry, 'value'> & {
48
- value: Uint8Array | string;
49
- };
50
-
51
- const REVALIDATED_TAGS_KEY = '__cacheComponents_revalidated_tags__';
52
- const SHARED_TAGS_KEY = '__cacheComponents_sharedTags__';
53
-
54
- let killContainerOnErrorCount = 0;
55
-
56
- export type CreateCacheComponentsHandlerOptions =
57
- CreateRedisStringsHandlerOptions & { serverDistDir?: string };
58
-
59
- async function streamToBuffer(
60
- stream: ReadableStream<Uint8Array>,
61
- ): Promise<Uint8Array> {
62
- const reader = stream.getReader();
63
- const chunks: Uint8Array[] = [];
64
-
65
- while (true) {
66
- const { value, done } = await reader.read();
67
- if (done) break;
68
- if (value) {
69
- chunks.push(value);
70
- }
71
- }
72
-
73
- if (chunks.length === 1) {
74
- return chunks[0];
75
- }
76
-
77
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
78
- const result = new Uint8Array(totalLength);
79
- let offset = 0;
80
- for (const chunk of chunks) {
81
- result.set(chunk, offset);
82
- offset += chunk.byteLength;
83
- }
84
- return result;
85
- }
86
-
87
- function bufferToReadableStream(
88
- buffer: Uint8Array,
89
- ): ReadableStream<Uint8Array> {
90
- return new ReadableStream<Uint8Array>({
91
- start(controller) {
92
- controller.enqueue(buffer);
93
- controller.close();
94
- },
95
- });
96
- }
97
-
98
- class RedisCacheComponentsHandler implements CacheComponentsHandler {
99
- private client: Client;
100
- private revalidatedTagsMap: SyncedMap<TagManifestEntry | number>;
101
- private sharedTagsMap: SyncedMap<string[]>;
102
- private inMemoryDeduplicationCache: SyncedMap<
103
- Promise<ReturnType<Client['get']>>
104
- >;
105
- private keyPrefix: string;
106
- private getTimeoutMs: number;
107
- private redisGet: Client['get'];
108
- private redisDeduplicationHandler: DeduplicatedRequestHandler<
109
- Client['get'],
110
- string | Buffer | null
111
- >;
112
- private deduplicatedRedisGet: (key: string) => Client['get'];
113
- private redisGetDeduplication: boolean;
114
- private inMemoryCachingTime: number;
115
- private redisConnectionDeferred: boolean;
116
-
117
- constructor({
118
- redisUrl = process.env.REDIS_URL
119
- ? process.env.REDIS_URL
120
- : process.env.REDISHOST
121
- ? `redis://${process.env.REDISHOST}:${process.env.REDISPORT}`
122
- : 'redis://localhost:6379',
123
- database = process.env.VERCEL_ENV === 'production' ? 0 : 1,
124
- keyPrefix,
125
- getTimeoutMs = process.env.REDIS_COMMAND_TIMEOUT_MS
126
- ? (Number.parseInt(process.env.REDIS_COMMAND_TIMEOUT_MS) ?? 500)
127
- : 500,
128
- revalidateTagQuerySize = 250,
129
- avgResyncIntervalMs = 60 * 60 * 1_000,
130
- redisGetDeduplication = true,
131
- inMemoryCachingTime = 10_000,
132
- socketOptions,
133
- clientOptions,
134
- killContainerOnErrorThreshold = process.env
135
- .KILL_CONTAINER_ON_ERROR_THRESHOLD
136
- ? (Number.parseInt(process.env.KILL_CONTAINER_ON_ERROR_THRESHOLD) ?? 0)
137
- : 0,
138
- serverDistDir,
139
- }: CreateCacheComponentsHandlerOptions) {
140
- try {
141
- this.keyPrefix = resolveKeyPrefix({
142
- optionKeyPrefix: keyPrefix,
143
- serverDistDir,
144
- env: process.env,
145
- });
146
- this.getTimeoutMs = getTimeoutMs;
147
- this.redisGetDeduplication = redisGetDeduplication;
148
- this.inMemoryCachingTime = inMemoryCachingTime;
149
- this.redisConnectionDeferred = shouldDeferRedisConnection();
150
-
151
- this.client = createClient({
152
- url: redisUrl,
153
- pingInterval: 10_000,
154
- ...(database !== 0 ? { database } : {}),
155
- ...(socketOptions
156
- ? { socket: { ...socketOptions } as RedisClientOptions['socket'] }
157
- : {}),
158
- ...(clientOptions || {}),
159
- });
160
-
161
- this.client.on('error', (error) => {
162
- console.error(
163
- 'RedisCacheComponentsHandler client error',
164
- error,
165
- killContainerOnErrorCount++,
166
- );
167
- setTimeout(() => {
168
- // node-redis throws "Socket already opened" if connect() is called while a socket is already open.
169
- // When we get an error while isOpen=true (but isReady=false), we should *not* force an extra connect.
170
- if (this.client.isOpen) return;
171
-
172
- this.client.connect().catch((err) => {
173
- console.error(
174
- 'Failed to reconnect RedisCacheComponentsHandler client after connection loss:',
175
- err,
176
- );
177
- });
178
- }, 1000);
179
- if (
180
- killContainerOnErrorThreshold > 0 &&
181
- killContainerOnErrorCount >= killContainerOnErrorThreshold
182
- ) {
183
- console.error(
184
- 'RedisCacheComponentsHandler client error threshold reached, disconnecting and exiting (please implement a restart process/container watchdog to handle this error)',
185
- error,
186
- killContainerOnErrorCount++,
187
- );
188
- this.client.disconnect();
189
- this.client.quit();
190
- setTimeout(() => {
191
- process.exit(1);
192
- }, 500);
193
- }
194
- });
195
-
196
- if (!this.redisConnectionDeferred) {
197
- this.client
198
- .connect()
199
- .then(() => {
200
- debug('green', 'RedisCacheComponentsHandler client connected.');
201
- })
202
- .catch(() => {
203
- this.client.connect().catch((error) => {
204
- console.error(
205
- 'Failed to connect RedisCacheComponentsHandler client:',
206
- error,
207
- );
208
- this.client.disconnect();
209
- throw error;
210
- });
211
- });
212
- }
213
-
214
- const filterKeys = (key: string): boolean =>
215
- key !== REVALIDATED_TAGS_KEY && key !== SHARED_TAGS_KEY;
216
-
217
- this.revalidatedTagsMap = new SyncedMap<TagManifestEntry | number>({
218
- client: this.client,
219
- keyPrefix: this.keyPrefix,
220
- redisKey: REVALIDATED_TAGS_KEY,
221
- database,
222
- querySize: revalidateTagQuerySize,
223
- filterKeys,
224
- customizedSync: { withoutOrphanCleanup: true },
225
- resyncIntervalMs:
226
- avgResyncIntervalMs +
227
- avgResyncIntervalMs / 10 +
228
- Math.random() * (avgResyncIntervalMs / 10),
229
- });
230
-
231
- this.sharedTagsMap = new SyncedMap<string[]>({
232
- client: this.client,
233
- keyPrefix: this.keyPrefix,
234
- redisKey: SHARED_TAGS_KEY,
235
- database,
236
- querySize: revalidateTagQuerySize,
237
- filterKeys,
238
- resyncIntervalMs:
239
- avgResyncIntervalMs -
240
- avgResyncIntervalMs / 10 +
241
- Math.random() * (avgResyncIntervalMs / 10),
242
- });
243
-
244
- this.inMemoryDeduplicationCache = new SyncedMap({
245
- client: this.client,
246
- keyPrefix: this.keyPrefix,
247
- redisKey: '__cacheComponents_inMemoryDeduplicationCache__',
248
- database,
249
- querySize: revalidateTagQuerySize,
250
- filterKeys,
251
- customizedSync: {
252
- withoutRedisHashmap: true,
253
- withoutSetSync: true,
254
- },
255
- });
256
-
257
- const redisGet: Client['get'] = this.client.get.bind(this.client);
258
- this.redisDeduplicationHandler = new DeduplicatedRequestHandler(
259
- redisGet,
260
- inMemoryCachingTime,
261
- this.inMemoryDeduplicationCache,
262
- );
263
- this.redisGet = redisGet;
264
- this.deduplicatedRedisGet =
265
- this.redisDeduplicationHandler.deduplicatedFunction;
266
- } catch (error) {
267
- console.error('RedisCacheComponentsHandler constructor error', error);
268
- throw error;
269
- }
270
- }
271
-
272
- private async assertClientIsReady(): Promise<void> {
273
- if (this.redisConnectionDeferred) {
274
- await Promise.all([
275
- this.revalidatedTagsMap.waitUntilReady(),
276
- this.sharedTagsMap.waitUntilReady(),
277
- ]);
278
- return;
279
- }
280
-
281
- if (!this.client.isReady && !this.client.isOpen) {
282
- await this.client.connect().catch((error) => {
283
- console.error(
284
- 'RedisCacheComponentsHandler assertClientIsReady reconnect error:',
285
- error,
286
- );
287
- throw error;
288
- });
289
- }
290
- await Promise.all([
291
- this.revalidatedTagsMap.waitUntilReady(),
292
- this.sharedTagsMap.waitUntilReady(),
293
- ]);
294
- }
295
-
296
- private isClientUnavailable(): boolean {
297
- return this.redisConnectionDeferred || !this.client.isReady;
298
- }
299
-
300
- private tagManifestFor(tag: string, now = Date.now()): TagManifestEntry {
301
- return normalizeTagManifest(this.revalidatedTagsMap.get(tag), now);
302
- }
303
-
304
- async get(
305
- cacheKey: string,
306
- _softTags: string[],
307
- ): Promise<CacheComponentsEntry | undefined> {
308
- // Soft-tag staleness is handled by Next.js via getExpiration() (implicit
309
- // `_N_T_` tags). Explicit cacheTag()s are checked below via the tag
310
- // manifest, matching DefaultCacheHandler.get() in Next.js 16.0–16.3.
311
- void _softTags;
312
-
313
- // Construct the full Redis key
314
- // For cache components, Next.js provides the full key including environment prefix
315
- // We prepend our keyPrefix for multi-tenant isolation
316
- const redisKey = `${this.keyPrefix}${cacheKey}`;
317
-
318
- try {
319
- await this.assertClientIsReady();
320
- if (this.isClientUnavailable()) {
321
- return undefined;
322
- }
323
-
324
- const readSerialized = (useDedup: boolean) =>
325
- redisErrorHandler(
326
- 'RedisCacheComponentsHandler.get(), operation: get' +
327
- (useDedup ? ' deduplicated' : '') +
328
- ' ' +
329
- this.getTimeoutMs +
330
- 'ms ' +
331
- redisKey,
332
- (useDedup ? this.deduplicatedRedisGet(cacheKey) : this.redisGet)(
333
- commandOptions({ signal: AbortSignal.timeout(this.getTimeoutMs) }),
334
- redisKey,
335
- ),
336
- );
337
-
338
- let usedDedup = this.redisGetDeduplication;
339
- let serialized = await readSerialized(usedDedup);
340
-
341
- for (let attempt = 0; attempt < 2; attempt++) {
342
- if (!serialized) {
343
- return undefined;
344
- }
345
-
346
- const stored: StoredCacheEntry = JSON.parse(serialized);
347
- const now = Date.now();
348
- const tags = stored.tags || [];
349
- const expiryTime = stored.timestamp + stored.expire * 1000;
350
- const ttlExpired =
351
- Number.isFinite(stored.expire) &&
352
- stored.expire > 0 &&
353
- now > expiryTime;
354
- const tagsExpired = areTagsExpired(tags, stored.timestamp, now, (tag) =>
355
- this.tagManifestFor(tag, now),
356
- );
357
-
358
- if (ttlExpired || tagsExpired) {
359
- if (usedDedup) {
360
- usedDedup = false;
361
- serialized = await readSerialized(false);
362
- continue;
363
- }
364
-
365
- const dropped = await compareAndUnlink(
366
- this.client,
367
- redisKey,
368
- serialized,
369
- );
370
- if (dropped) {
371
- await this.sharedTagsMap.delete(cacheKey).catch(() => {});
372
- }
373
- return undefined;
374
- }
375
-
376
- const valueBuffer =
377
- typeof stored.value === 'string'
378
- ? new Uint8Array(Buffer.from(stored.value, 'base64'))
379
- : stored.value;
380
-
381
- const entry: CacheComponentsEntry = {
382
- ...stored,
383
- value: bufferToReadableStream(valueBuffer),
384
- };
385
-
386
- if (
387
- areTagsStale(tags, stored.timestamp, (tag) =>
388
- this.tagManifestFor(tag, now),
389
- )
390
- ) {
391
- entry.revalidate = -1;
392
- }
393
-
394
- return entry;
395
- }
396
-
397
- return undefined;
398
- } catch (error) {
399
- console.error(
400
- 'RedisCacheComponentsHandler.get() Error occurred while getting cache entry. Returning undefined so site can continue to serve content while cache is disabled. The original error was:',
401
- error,
402
- killContainerOnErrorCount++,
403
- );
404
- return undefined;
405
- }
406
- }
407
-
408
- async set(
409
- cacheKey: string,
410
- pendingEntry: Promise<CacheComponentsEntry>,
411
- ): Promise<void> {
412
- try {
413
- await this.assertClientIsReady();
414
- if (this.isClientUnavailable()) {
415
- return;
416
- }
417
-
418
- const entry = await pendingEntry;
419
-
420
- const [storeStream] = entry.value.tee();
421
-
422
- // Don't mutate entry.value as Next.js may still be using it internally
423
- // entry.value = forwardStream;
424
-
425
- const buffer = await streamToBuffer(storeStream);
426
-
427
- const stored: StoredCacheEntry = {
428
- value: Buffer.from(buffer).toString('base64'),
429
- tags: entry.tags || [],
430
- stale: entry.stale,
431
- timestamp: entry.timestamp,
432
- expire: entry.expire,
433
- revalidate: entry.revalidate,
434
- };
435
-
436
- let serialized: string;
437
- try {
438
- const cleanStored = {
439
- value: stored.value,
440
- tags: Array.isArray(stored.tags) ? [...stored.tags] : [],
441
- stale: Number(stored.stale),
442
- timestamp: Number(stored.timestamp),
443
- expire: Number(stored.expire),
444
- revalidate: Number(stored.revalidate),
445
- };
446
- serialized = JSON.stringify(cleanStored);
447
- } catch (jsonError) {
448
- console.error('JSON.stringify error:', jsonError);
449
- console.error('Stored object:', stored);
450
- throw jsonError;
451
- }
452
-
453
- if (this.redisGetDeduplication) {
454
- this.redisDeduplicationHandler.seedRequestReturn(cacheKey, serialized);
455
- }
456
-
457
- // expire is already a duration in seconds, use it directly
458
- const ttlSeconds =
459
- Number.isFinite(stored.expire) && stored.expire > 0
460
- ? Math.floor(stored.expire)
461
- : undefined;
462
-
463
- const redisKey = `${this.keyPrefix}${cacheKey}`;
464
-
465
- const setOperation = redisErrorHandler(
466
- 'RedisCacheComponentsHandler.set(), operation: set ' + redisKey,
467
- this.client.set(redisKey, serialized, {
468
- ...(ttlSeconds ? { EX: ttlSeconds } : {}),
469
- }),
470
- );
471
-
472
- let tagsOperation: Promise<void> | undefined;
473
- const tags = stored.tags || [];
474
- if (tags.length > 0) {
475
- const currentTags = this.sharedTagsMap.get(cacheKey);
476
- const currentIsSameAsNew =
477
- currentTags?.length === tags.length &&
478
- currentTags.every((v) => tags.includes(v)) &&
479
- tags.every((v) => currentTags!.includes(v));
480
-
481
- if (!currentIsSameAsNew) {
482
- tagsOperation = this.sharedTagsMap.set(cacheKey, [...tags]);
483
- }
484
- }
485
-
486
- await Promise.all([setOperation, tagsOperation]);
487
- } catch (error) {
488
- console.error(
489
- 'RedisCacheComponentsHandler.set() Error occurred while setting cache entry. The original error was:',
490
- error,
491
- killContainerOnErrorCount++,
492
- );
493
- throw error;
494
- }
495
- }
496
-
497
- async refreshTags(): Promise<void> {
498
- await this.assertClientIsReady();
499
- }
500
-
501
- async getExpiration(tags: string[]): Promise<number> {
502
- try {
503
- await this.assertClientIsReady();
504
- return maxExpiredTimestamp(tags || [], (tag) => this.tagManifestFor(tag));
505
- } catch (error) {
506
- console.error(
507
- 'RedisCacheComponentsHandler.getExpiration() Error occurred while getting expiration for tags. The original error was:',
508
- error,
509
- );
510
- return 0;
511
- }
512
- }
513
-
514
- async updateTags(
515
- tags: string[],
516
- durations?: { expire?: number },
517
- ): Promise<void> {
518
- try {
519
- await this.assertClientIsReady();
520
- if (this.isClientUnavailable()) {
521
- return;
522
- }
523
-
524
- const now = Date.now();
525
- const updated = new Set(tags || []);
526
- for (const tag of updated) {
527
- const next = applyTagUpdate(this.tagManifestFor(tag), durations, now);
528
- await this.revalidatedTagsMap.set(
529
- tag,
530
- persistableTagManifest(next, now),
531
- );
532
- }
533
-
534
- if (this.redisGetDeduplication && this.inMemoryCachingTime > 0) {
535
- for (const [key, sharedTags] of this.sharedTagsMap.entries()) {
536
- if (sharedTags.some((tag) => updated.has(tag))) {
537
- await this.inMemoryDeduplicationCache.delete(key, true);
538
- }
539
- }
540
- }
541
- } catch (error) {
542
- console.error(
543
- 'RedisCacheComponentsHandler.updateTags() Error occurred while updating tags. The original error was:',
544
- error,
545
- killContainerOnErrorCount++,
546
- );
547
- throw error;
548
- }
549
- }
550
- }
551
-
552
- let singletonHandler: CacheComponentsHandler | undefined;
553
-
554
- export function getRedisCacheComponentsHandler(
555
- options: CreateCacheComponentsHandlerOptions = {},
556
- ): CacheComponentsHandler {
557
- if (!singletonHandler) {
558
- singletonHandler = new RedisCacheComponentsHandler(options);
559
- }
560
- return singletonHandler;
561
- }
562
-
563
- // Lazily resolve the default Cache Components handler.
564
- //
565
- // Constructing a RedisCacheComponentsHandler opens a Redis connection in its
566
- // constructor. Building the singleton at module-eval time therefore means that simply
567
- // *importing this package* — e.g. only for `RedisStringsHandler` (the legacy
568
- // `cacheHandler`), with Cache Components never enabled — eagerly connects to Redis,
569
- // defaulting to `redis://localhost:6379` when neither `REDIS_URL` nor `REDISHOST` is
570
- // set. In a deployment whose Redis is not on localhost that yields a non-stop
571
- // `RedisCacheComponentsHandler client error ECONNREFUSED 127.0.0.1:6379` reconnect
572
- // loop, and it also makes a consumer's later `getRedisCacheComponentsHandler(options)`
573
- // a no-op, because the singleton was already built with defaults (see #84).
574
- //
575
- // Defer construction to first use via a Proxy: importing the package never connects, a
576
- // consumer that configures the handler via `getRedisCacheComponentsHandler(options)`
577
- // before it is first used has that configuration honored, and a consumer that never
578
- // touches Cache Components never opens a Redis connection at all.
579
- let resolvedHandler: CacheComponentsHandler | undefined;
580
-
581
- export const redisCacheHandler: CacheComponentsHandler = new Proxy(
582
- {} as CacheComponentsHandler,
583
- {
584
- get(_target, prop) {
585
- if (!resolvedHandler) {
586
- resolvedHandler = getRedisCacheComponentsHandler();
587
- }
588
- const value = resolvedHandler[prop as keyof CacheComponentsHandler];
589
- return typeof value === 'function'
590
- ? (value as (...args: unknown[]) => unknown).bind(resolvedHandler)
591
- : value;
592
- },
593
- has(_target, prop) {
594
- return prop in RedisCacheComponentsHandler.prototype;
595
- },
596
- },
597
- );
@@ -1,53 +0,0 @@
1
- import RedisStringsHandler, {
2
- CreateRedisStringsHandlerOptions,
3
- } from './RedisStringsHandler';
4
- import { debugVerbose } from './utils/debug';
5
- import { resolveKeyPrefix } from './utils/prefix';
6
-
7
- let cachedHandler: RedisStringsHandler;
8
-
9
- type NextCacheHandlerOptions = CreateRedisStringsHandlerOptions & {
10
- serverDistDir?: string;
11
- };
12
-
13
- export default class CachedHandler {
14
- constructor(options: NextCacheHandlerOptions) {
15
- if (!cachedHandler) {
16
- console.log('created cached handler');
17
- const keyPrefix = resolveKeyPrefix({
18
- optionKeyPrefix: options.keyPrefix,
19
- serverDistDir: options.serverDistDir,
20
- env: process.env,
21
- });
22
-
23
- cachedHandler = new RedisStringsHandler({
24
- ...options,
25
- keyPrefix,
26
- });
27
- }
28
- }
29
- get(
30
- ...args: Parameters<RedisStringsHandler['get']>
31
- ): ReturnType<RedisStringsHandler['get']> {
32
- debugVerbose('CachedHandler.get called with', args);
33
- return cachedHandler.get(...args);
34
- }
35
- set(
36
- ...args: Parameters<RedisStringsHandler['set']>
37
- ): ReturnType<RedisStringsHandler['set']> {
38
- debugVerbose('CachedHandler.set called with', args);
39
- return cachedHandler.set(...args);
40
- }
41
- revalidateTag(
42
- ...args: Parameters<RedisStringsHandler['revalidateTag']>
43
- ): ReturnType<RedisStringsHandler['revalidateTag']> {
44
- debugVerbose('CachedHandler.revalidateTag called with', args);
45
- return cachedHandler.revalidateTag(...args);
46
- }
47
- resetRequestCache(
48
- ...args: Parameters<RedisStringsHandler['resetRequestCache']>
49
- ): ReturnType<RedisStringsHandler['resetRequestCache']> {
50
- // debug("CachedHandler.resetRequestCache called with", args);
51
- return cachedHandler.resetRequestCache(...args);
52
- }
53
- }