@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/dist/index.js CHANGED
@@ -36,7 +36,9 @@ __export(index_exports, {
36
36
  default: () => index_default,
37
37
  getRedisCacheComponentsHandler: () => getRedisCacheComponentsHandler,
38
38
  jsonCacheValueSerializer: () => jsonCacheValueSerializer,
39
- redisCacheHandler: () => redisCacheHandler
39
+ redisCacheHandler: () => redisCacheHandler,
40
+ resolveCacheEntryTtlSeconds: () => resolveCacheEntryTtlSeconds,
41
+ shouldDeferRedisConnection: () => shouldDeferRedisConnection
40
42
  });
41
43
  module.exports = __toCommonJS(index_exports);
42
44
 
@@ -64,6 +66,12 @@ function debugVerbose(color, ...args) {
64
66
  }
65
67
  }
66
68
 
69
+ // src/utils/redisConnection.ts
70
+ var PHASE_PRODUCTION_BUILD = "phase-production-build";
71
+ function shouldDeferRedisConnection() {
72
+ return process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD;
73
+ }
74
+
67
75
  // src/SyncedMap.ts
68
76
  var SYNC_CHANNEL_SUFFIX = ":sync-channel:";
69
77
  var SyncedMap = class {
@@ -89,6 +97,10 @@ var SyncedMap = class {
89
97
  });
90
98
  }
91
99
  async setup() {
100
+ if (shouldDeferRedisConnection()) {
101
+ this.setupLockResolve();
102
+ return;
103
+ }
92
104
  let setupPromises = [];
93
105
  if (!this.customizedSync?.withoutRedisHashmap) {
94
106
  setupPromises.push(this.initialSync());
@@ -99,45 +111,54 @@ var SyncedMap = class {
99
111
  this.setupLockResolve();
100
112
  }
101
113
  async initialSync() {
102
- let cursor = 0;
103
- const hScanOptions = { COUNT: this.querySize };
114
+ let cursor = "0";
104
115
  try {
105
116
  do {
106
- const remoteItems = await redisErrorHandler(
107
- "SyncedMap.initialSync(), operation: hScan " + this.syncChannel + " " + this.keyPrefix + " " + this.redisKey + " " + cursor + " " + this.querySize,
108
- this.client.hScan(
117
+ const [nextCursor, tuples] = await redisErrorHandler(
118
+ "SyncedMap.initialSync(), operation: HSCAN " + this.keyPrefix + this.redisKey,
119
+ this.client.sendCommand([
120
+ "HSCAN",
109
121
  this.keyPrefix + this.redisKey,
110
122
  cursor,
111
- hScanOptions
112
- )
123
+ "COUNT",
124
+ String(this.querySize)
125
+ ])
113
126
  );
114
- for (const { field, value } of remoteItems.tuples) {
127
+ for (let i = 0; i < tuples.length; i += 2) {
128
+ const field = tuples[i];
115
129
  if (this.filterKeys(field)) {
116
- const parsedValue = JSON.parse(value);
117
- this.map.set(field, parsedValue);
130
+ this.map.set(field, JSON.parse(tuples[i + 1]));
118
131
  }
119
132
  }
120
- cursor = remoteItems.cursor;
121
- } while (cursor !== 0);
122
- await this.cleanupKeysNotInRedis();
133
+ cursor = nextCursor;
134
+ } while (cursor !== "0");
135
+ if (!this.customizedSync?.withoutOrphanCleanup) {
136
+ await this.cleanupKeysNotInRedis();
137
+ }
123
138
  } catch (error) {
124
139
  console.error("Error during initial sync:", error);
125
140
  throw error;
126
141
  }
127
142
  }
128
143
  async cleanupKeysNotInRedis() {
129
- let cursor = 0;
130
- const scanOptions = { COUNT: this.querySize, MATCH: `${this.keyPrefix}*` };
144
+ let cursor = "0";
131
145
  let remoteKeys = [];
132
146
  try {
133
147
  do {
134
- const remoteKeysPortion = await redisErrorHandler(
135
- "SyncedMap.cleanupKeysNotInRedis(), operation: scan " + this.keyPrefix,
136
- this.client.scan(cursor, scanOptions)
148
+ const [nextCursor, keys] = await redisErrorHandler(
149
+ "SyncedMap.cleanupKeysNotInRedis(), operation: SCAN " + this.keyPrefix,
150
+ this.client.sendCommand([
151
+ "SCAN",
152
+ cursor,
153
+ "MATCH",
154
+ `${this.keyPrefix}*`,
155
+ "COUNT",
156
+ String(this.querySize)
157
+ ])
137
158
  );
138
- remoteKeys = remoteKeys.concat(remoteKeysPortion.keys);
139
- cursor = remoteKeysPortion.cursor;
140
- } while (cursor !== 0);
159
+ remoteKeys = remoteKeys.concat(keys);
160
+ cursor = nextCursor;
161
+ } while (cursor !== "0");
141
162
  const remoteKeysSet = new Set(
142
163
  remoteKeys.map((key) => key.substring(this.keyPrefix.length))
143
164
  );
@@ -531,6 +552,37 @@ var jsonCacheValueSerializer = {
531
552
  }
532
553
  };
533
554
 
555
+ // src/utils/cacheTtl.ts
556
+ function resolveCacheEntryTtlSeconds(ctx, data, options) {
557
+ const expire = ctx.cacheControl?.expire;
558
+ if (Number.isFinite(expire) && expire > 0) {
559
+ return Math.max(1, Math.ceil(expire));
560
+ }
561
+ const revalidate = resolveRevalidateValue(ctx, data);
562
+ if (revalidate === false) {
563
+ return options.estimateExpireAge(options.defaultStaleAge);
564
+ }
565
+ if (revalidate && Number.isSafeInteger(revalidate) && revalidate > 0) {
566
+ return options.estimateExpireAge(revalidate);
567
+ }
568
+ return void 0;
569
+ }
570
+ function resolveRevalidateValue(ctx, data) {
571
+ if (data?.kind === "FETCH" && data.revalidate !== void 0) {
572
+ return data.revalidate;
573
+ }
574
+ if (ctx.revalidate !== void 0) {
575
+ return ctx.revalidate;
576
+ }
577
+ if (ctx.cacheControl?.revalidate !== void 0) {
578
+ return ctx.cacheControl.revalidate;
579
+ }
580
+ if (data?.revalidate !== void 0) {
581
+ return data.revalidate;
582
+ }
583
+ return void 0;
584
+ }
585
+
534
586
  // src/RedisStringsHandler.ts
535
587
  function redisErrorHandler(debugInfo, redisCommandResult) {
536
588
  const beforeTimestamp = performance.now();
@@ -608,6 +660,7 @@ var RedisStringsHandler = class {
608
660
  this.killContainerOnErrorThreshold = killContainerOnErrorThreshold;
609
661
  this.getTimeoutMs = getTimeoutMs;
610
662
  this.valueSerializer = valueSerializer;
663
+ this.redisConnectionDeferred = shouldDeferRedisConnection();
611
664
  try {
612
665
  this.client = (0, import_redis.createClient)({
613
666
  url: redisUrl,
@@ -646,12 +699,14 @@ var RedisStringsHandler = class {
646
699
  }, 500);
647
700
  }
648
701
  });
649
- this.client.connect().then(() => {
650
- debug("green", "Redis client connected.");
651
- }).catch((error) => {
652
- console.error("Failed to connect Redis client:", error);
653
- throw error;
654
- });
702
+ if (!this.redisConnectionDeferred) {
703
+ this.client.connect().then(() => {
704
+ debug("green", "Redis client connected.");
705
+ }).catch((error) => {
706
+ console.error("Failed to connect Redis client:", error);
707
+ throw error;
708
+ });
709
+ }
655
710
  } catch (error) {
656
711
  console.error("Failed to initialize Redis client");
657
712
  throw error;
@@ -673,6 +728,7 @@ var RedisStringsHandler = class {
673
728
  database,
674
729
  querySize: revalidateTagQuerySize,
675
730
  filterKeys,
731
+ customizedSync: { withoutOrphanCleanup: true },
676
732
  resyncIntervalMs: avgResyncIntervalMs + avgResyncIntervalMs / 10 + Math.random() * (avgResyncIntervalMs / 10)
677
733
  });
678
734
  this.inMemoryDeduplicationCache = new SyncedMap({
@@ -742,11 +798,17 @@ var RedisStringsHandler = class {
742
798
  }
743
799
  this.clientReadyCalls = 0;
744
800
  if (!this.client.isReady) {
801
+ if (this.redisConnectionDeferred) {
802
+ return;
803
+ }
745
804
  throw new Error(
746
805
  "assertClientIsReady: Redis client is not ready yet or connection is lost."
747
806
  );
748
807
  }
749
808
  }
809
+ isClientUnavailable() {
810
+ return this.redisConnectionDeferred || !this.client.isReady;
811
+ }
750
812
  async get(key, ctx) {
751
813
  try {
752
814
  if (!SUPPORTED_GET_KINDS.includes(ctx.kind)) {
@@ -760,6 +822,9 @@ var RedisStringsHandler = class {
760
822
  }
761
823
  debug("green", "RedisStringsHandler.get() called with", key, ctx);
762
824
  await this.assertClientIsReady();
825
+ if (this.isClientUnavailable()) {
826
+ return null;
827
+ }
763
828
  const clientGet = this.redisGetDeduplication ? this.deduplicatedRedisGet(key) : this.redisGet;
764
829
  const redisGetOperation = clientGet(
765
830
  (0, import_redis.commandOptions)({ signal: AbortSignal.timeout(this.getTimeoutMs) }),
@@ -893,6 +958,9 @@ var RedisStringsHandler = class {
893
958
  );
894
959
  }
895
960
  await this.assertClientIsReady();
961
+ if (this.isClientUnavailable()) {
962
+ return;
963
+ }
896
964
  if (data?.kind === "APP_PAGE" || data?.kind === "APP_ROUTE") {
897
965
  const tags = data.headers["x-next-cache-tags"]?.split(",");
898
966
  ctx.tags = [...ctx.tags || [], ...tags || []];
@@ -915,18 +983,14 @@ var RedisStringsHandler = class {
915
983
  serializedCacheEntry
916
984
  );
917
985
  }
918
- const revalidate = (
919
- // For fetch requests in newest versions, the revalidate context property is never used, and instead the revalidate property of the passed-in data is used
920
- data?.kind === "FETCH" && data.revalidate || ctx.revalidate || ctx.cacheControl?.revalidate || // Legacy fallback: older Next.js versions attached `revalidate` directly
921
- // to the data payload. FETCH is already handled above, so this only
922
- // matters for those older, non-discriminated shapes.
923
- data?.revalidate
924
- );
925
- const expireAt = revalidate && Number.isSafeInteger(revalidate) && revalidate > 0 ? this.estimateExpireAge(revalidate) : this.estimateExpireAge(this.defaultStaleAge);
986
+ const expireAt = resolveCacheEntryTtlSeconds(ctx, data, {
987
+ estimateExpireAge: this.estimateExpireAge,
988
+ defaultStaleAge: this.defaultStaleAge
989
+ });
926
990
  const setOperation = redisErrorHandler(
927
991
  "RedisStringsHandler.set(), operation: set " + this.keyPrefix + " " + key,
928
992
  this.client.set(this.keyPrefix + key, serializedCacheEntry, {
929
- EX: expireAt
993
+ ...expireAt ? { EX: expireAt } : {}
930
994
  })
931
995
  );
932
996
  debug(
@@ -989,6 +1053,9 @@ var RedisStringsHandler = class {
989
1053
  );
990
1054
  const tags = new Set([tagOrTags || []].flat());
991
1055
  await this.assertClientIsReady();
1056
+ if (this.isClientUnavailable()) {
1057
+ return;
1058
+ }
992
1059
  const keysToDelete = /* @__PURE__ */ new Set();
993
1060
  for (const tag of tags) {
994
1061
  if (tag.startsWith(NEXT_CACHE_IMPLICIT_TAG_ID)) {
@@ -1127,6 +1194,88 @@ var CachedHandler = class {
1127
1194
 
1128
1195
  // src/CacheComponentsHandler.ts
1129
1196
  var import_redis2 = require("redis");
1197
+
1198
+ // src/utils/compareAndUnlink.ts
1199
+ var COMPARE_AND_UNLINK = `
1200
+ if redis.call('GET', KEYS[1]) == ARGV[1] then
1201
+ return redis.call('UNLINK', KEYS[1])
1202
+ end
1203
+ return 0
1204
+ `;
1205
+ async function compareAndUnlink(client, key, expected) {
1206
+ const deleted = await client.eval(COMPARE_AND_UNLINK, {
1207
+ keys: [key],
1208
+ arguments: [expected]
1209
+ });
1210
+ return deleted === 1 || deleted === 1n;
1211
+ }
1212
+
1213
+ // src/utils/tagRevalidation.ts
1214
+ function normalizeTagManifest(stored, now = Date.now()) {
1215
+ if (typeof stored === "number" && Number.isFinite(stored)) {
1216
+ return { expired: Math.min(stored, now) };
1217
+ }
1218
+ if (stored && typeof stored === "object") {
1219
+ const rec = stored;
1220
+ const stale = typeof rec.stale === "number" && Number.isFinite(rec.stale) ? rec.stale : typeof rec.last === "number" && Number.isFinite(rec.last) ? rec.last : void 0;
1221
+ const expired = typeof rec.expired === "number" && Number.isFinite(rec.expired) ? rec.expired : typeof rec.pending === "number" && Number.isFinite(rec.pending) ? rec.pending : void 0;
1222
+ return {
1223
+ ...stale !== void 0 ? { stale } : {},
1224
+ ...expired !== void 0 ? { expired } : {}
1225
+ };
1226
+ }
1227
+ return {};
1228
+ }
1229
+ function applyTagUpdate(existing, durations, now) {
1230
+ if (durations) {
1231
+ const updates = { ...existing };
1232
+ updates.stale = now;
1233
+ if (durations.expire !== void 0) {
1234
+ updates.expired = now + durations.expire * 1e3;
1235
+ }
1236
+ return updates;
1237
+ }
1238
+ return {
1239
+ ...existing,
1240
+ expired: now
1241
+ };
1242
+ }
1243
+ function persistableTagManifest(entry, now) {
1244
+ if (typeof entry.expired === "number" && entry.expired <= now && (entry.stale === void 0 || entry.stale <= entry.expired)) {
1245
+ return entry.expired;
1246
+ }
1247
+ return entry;
1248
+ }
1249
+ function areTagsExpired(tags, entryTimestamp, now, getEntry) {
1250
+ for (const tag of tags) {
1251
+ const expiredAt = getEntry(tag)?.expired;
1252
+ if (typeof expiredAt === "number" && expiredAt <= now && expiredAt > entryTimestamp) {
1253
+ return true;
1254
+ }
1255
+ }
1256
+ return false;
1257
+ }
1258
+ function areTagsStale(tags, entryTimestamp, getEntry) {
1259
+ for (const tag of tags) {
1260
+ const staleAt = getEntry(tag)?.stale ?? 0;
1261
+ if (typeof staleAt === "number" && staleAt > entryTimestamp) {
1262
+ return true;
1263
+ }
1264
+ }
1265
+ return false;
1266
+ }
1267
+ function maxExpiredTimestamp(tags, getEntry) {
1268
+ let max = 0;
1269
+ for (const tag of tags) {
1270
+ const expired = getEntry(tag)?.expired || 0;
1271
+ if (expired > max) {
1272
+ max = expired;
1273
+ }
1274
+ }
1275
+ return max;
1276
+ }
1277
+
1278
+ // src/CacheComponentsHandler.ts
1130
1279
  var REVALIDATED_TAGS_KEY2 = "__cacheComponents_revalidated_tags__";
1131
1280
  var SHARED_TAGS_KEY = "__cacheComponents_sharedTags__";
1132
1281
  var killContainerOnErrorCount2 = 0;
@@ -1184,6 +1333,7 @@ var RedisCacheComponentsHandler = class {
1184
1333
  this.getTimeoutMs = getTimeoutMs;
1185
1334
  this.redisGetDeduplication = redisGetDeduplication;
1186
1335
  this.inMemoryCachingTime = inMemoryCachingTime;
1336
+ this.redisConnectionDeferred = shouldDeferRedisConnection();
1187
1337
  this.client = (0, import_redis2.createClient)({
1188
1338
  url: redisUrl,
1189
1339
  pingInterval: 1e4,
@@ -1219,18 +1369,20 @@ var RedisCacheComponentsHandler = class {
1219
1369
  }, 500);
1220
1370
  }
1221
1371
  });
1222
- this.client.connect().then(() => {
1223
- debug("green", "RedisCacheComponentsHandler client connected.");
1224
- }).catch(() => {
1225
- this.client.connect().catch((error) => {
1226
- console.error(
1227
- "Failed to connect RedisCacheComponentsHandler client:",
1228
- error
1229
- );
1230
- this.client.disconnect();
1231
- throw error;
1372
+ if (!this.redisConnectionDeferred) {
1373
+ this.client.connect().then(() => {
1374
+ debug("green", "RedisCacheComponentsHandler client connected.");
1375
+ }).catch(() => {
1376
+ this.client.connect().catch((error) => {
1377
+ console.error(
1378
+ "Failed to connect RedisCacheComponentsHandler client:",
1379
+ error
1380
+ );
1381
+ this.client.disconnect();
1382
+ throw error;
1383
+ });
1232
1384
  });
1233
- });
1385
+ }
1234
1386
  const filterKeys = (key) => key !== REVALIDATED_TAGS_KEY2 && key !== SHARED_TAGS_KEY;
1235
1387
  this.revalidatedTagsMap = new SyncedMap({
1236
1388
  client: this.client,
@@ -1239,6 +1391,7 @@ var RedisCacheComponentsHandler = class {
1239
1391
  database,
1240
1392
  querySize: revalidateTagQuerySize,
1241
1393
  filterKeys,
1394
+ customizedSync: { withoutOrphanCleanup: true },
1242
1395
  resyncIntervalMs: avgResyncIntervalMs + avgResyncIntervalMs / 10 + Math.random() * (avgResyncIntervalMs / 10)
1243
1396
  });
1244
1397
  this.sharedTagsMap = new SyncedMap({
@@ -1276,6 +1429,13 @@ var RedisCacheComponentsHandler = class {
1276
1429
  }
1277
1430
  }
1278
1431
  async assertClientIsReady() {
1432
+ if (this.redisConnectionDeferred) {
1433
+ await Promise.all([
1434
+ this.revalidatedTagsMap.waitUntilReady(),
1435
+ this.sharedTagsMap.waitUntilReady()
1436
+ ]);
1437
+ return;
1438
+ }
1279
1439
  if (!this.client.isReady && !this.client.isOpen) {
1280
1440
  await this.client.connect().catch((error) => {
1281
1441
  console.error(
@@ -1290,57 +1450,76 @@ var RedisCacheComponentsHandler = class {
1290
1450
  this.sharedTagsMap.waitUntilReady()
1291
1451
  ]);
1292
1452
  }
1293
- async computeMaxRevalidation(tags) {
1294
- let max = 0;
1295
- for (const tag of tags) {
1296
- const ts = this.revalidatedTagsMap.get(tag);
1297
- if (ts && ts > max) {
1298
- max = ts;
1299
- }
1300
- }
1301
- return max;
1453
+ isClientUnavailable() {
1454
+ return this.redisConnectionDeferred || !this.client.isReady;
1455
+ }
1456
+ tagManifestFor(tag, now = Date.now()) {
1457
+ return normalizeTagManifest(this.revalidatedTagsMap.get(tag), now);
1302
1458
  }
1303
- async get(cacheKey, softTags) {
1459
+ async get(cacheKey, _softTags) {
1460
+ void _softTags;
1304
1461
  const redisKey = `${this.keyPrefix}${cacheKey}`;
1305
1462
  try {
1306
1463
  await this.assertClientIsReady();
1307
- const clientGet = this.redisGetDeduplication ? this.deduplicatedRedisGet(cacheKey) : this.redisGet;
1308
- const serialized = await redisErrorHandler(
1309
- "RedisCacheComponentsHandler.get(), operation: get" + (this.redisGetDeduplication ? " deduplicated" : "") + " " + this.getTimeoutMs + "ms " + redisKey,
1310
- clientGet(
1464
+ if (this.isClientUnavailable()) {
1465
+ return void 0;
1466
+ }
1467
+ const readSerialized = (useDedup) => redisErrorHandler(
1468
+ "RedisCacheComponentsHandler.get(), operation: get" + (useDedup ? " deduplicated" : "") + " " + this.getTimeoutMs + "ms " + redisKey,
1469
+ (useDedup ? this.deduplicatedRedisGet(cacheKey) : this.redisGet)(
1311
1470
  (0, import_redis2.commandOptions)({ signal: AbortSignal.timeout(this.getTimeoutMs) }),
1312
1471
  redisKey
1313
1472
  )
1314
1473
  );
1315
- if (!serialized) {
1316
- return void 0;
1317
- }
1318
- const stored = JSON.parse(serialized);
1319
- const now = Date.now();
1320
- const expiryTime = stored.timestamp + stored.expire * 1e3;
1321
- if (Number.isFinite(stored.expire) && stored.expire > 0 && now > expiryTime) {
1322
- await this.client.unlink(redisKey).catch(() => {
1323
- });
1324
- await this.sharedTagsMap.delete(cacheKey).catch(() => {
1325
- });
1326
- return void 0;
1327
- }
1328
- const maxRevalidation = await this.computeMaxRevalidation([
1329
- ...stored.tags || [],
1330
- ...softTags || []
1331
- ]);
1332
- if (maxRevalidation > 0 && maxRevalidation > stored.timestamp) {
1333
- await this.client.unlink(redisKey).catch(() => {
1334
- });
1335
- await this.sharedTagsMap.delete(cacheKey).catch(() => {
1336
- });
1337
- return void 0;
1474
+ let usedDedup = this.redisGetDeduplication;
1475
+ let serialized = await readSerialized(usedDedup);
1476
+ for (let attempt = 0; attempt < 2; attempt++) {
1477
+ if (!serialized) {
1478
+ return void 0;
1479
+ }
1480
+ const stored = JSON.parse(serialized);
1481
+ const now = Date.now();
1482
+ const tags = stored.tags || [];
1483
+ const expiryTime = stored.timestamp + stored.expire * 1e3;
1484
+ const ttlExpired = Number.isFinite(stored.expire) && stored.expire > 0 && now > expiryTime;
1485
+ const tagsExpired = areTagsExpired(
1486
+ tags,
1487
+ stored.timestamp,
1488
+ now,
1489
+ (tag) => this.tagManifestFor(tag, now)
1490
+ );
1491
+ if (ttlExpired || tagsExpired) {
1492
+ if (usedDedup) {
1493
+ usedDedup = false;
1494
+ serialized = await readSerialized(false);
1495
+ continue;
1496
+ }
1497
+ const dropped = await compareAndUnlink(
1498
+ this.client,
1499
+ redisKey,
1500
+ serialized
1501
+ );
1502
+ if (dropped) {
1503
+ await this.sharedTagsMap.delete(cacheKey).catch(() => {
1504
+ });
1505
+ }
1506
+ return void 0;
1507
+ }
1508
+ const valueBuffer = typeof stored.value === "string" ? new Uint8Array(Buffer.from(stored.value, "base64")) : stored.value;
1509
+ const entry = {
1510
+ ...stored,
1511
+ value: bufferToReadableStream(valueBuffer)
1512
+ };
1513
+ if (areTagsStale(
1514
+ tags,
1515
+ stored.timestamp,
1516
+ (tag) => this.tagManifestFor(tag, now)
1517
+ )) {
1518
+ entry.revalidate = -1;
1519
+ }
1520
+ return entry;
1338
1521
  }
1339
- const valueBuffer = typeof stored.value === "string" ? new Uint8Array(Buffer.from(stored.value, "base64")) : stored.value;
1340
- return {
1341
- ...stored,
1342
- value: bufferToReadableStream(valueBuffer)
1343
- };
1522
+ return void 0;
1344
1523
  } catch (error) {
1345
1524
  console.error(
1346
1525
  "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:",
@@ -1353,6 +1532,9 @@ var RedisCacheComponentsHandler = class {
1353
1532
  async set(cacheKey, pendingEntry) {
1354
1533
  try {
1355
1534
  await this.assertClientIsReady();
1535
+ if (this.isClientUnavailable()) {
1536
+ return;
1537
+ }
1356
1538
  const entry = await pendingEntry;
1357
1539
  const [storeStream] = entry.value.tee();
1358
1540
  const buffer = await streamToBuffer(storeStream);
@@ -1416,7 +1598,7 @@ var RedisCacheComponentsHandler = class {
1416
1598
  async getExpiration(tags) {
1417
1599
  try {
1418
1600
  await this.assertClientIsReady();
1419
- return this.computeMaxRevalidation(tags || []);
1601
+ return maxExpiredTimestamp(tags || [], (tag) => this.tagManifestFor(tag));
1420
1602
  } catch (error) {
1421
1603
  console.error(
1422
1604
  "RedisCacheComponentsHandler.getExpiration() Error occurred while getting expiration for tags. The original error was:",
@@ -1425,37 +1607,28 @@ var RedisCacheComponentsHandler = class {
1425
1607
  return 0;
1426
1608
  }
1427
1609
  }
1428
- async updateTags(tags, _durations) {
1610
+ async updateTags(tags, durations) {
1429
1611
  try {
1430
- void _durations;
1431
1612
  await this.assertClientIsReady();
1432
- const now = Date.now();
1433
- const tagsSet = new Set(tags || []);
1434
- for (const tag of tagsSet) {
1435
- await this.revalidatedTagsMap.set(tag, now);
1436
- }
1437
- const keysToDelete = /* @__PURE__ */ new Set();
1438
- for (const [key, storedTags] of this.sharedTagsMap.entries()) {
1439
- if (storedTags.some((tag) => tagsSet.has(tag))) {
1440
- keysToDelete.add(key);
1441
- }
1442
- }
1443
- if (keysToDelete.size === 0) {
1613
+ if (this.isClientUnavailable()) {
1444
1614
  return;
1445
1615
  }
1446
- const cacheKeys = Array.from(keysToDelete);
1447
- const fullRedisKeys = cacheKeys.map((key) => `${this.keyPrefix}${key}`);
1448
- await redisErrorHandler(
1449
- "RedisCacheComponentsHandler.updateTags(), operation: unlink",
1450
- this.client.unlink(fullRedisKeys)
1451
- );
1616
+ const now = Date.now();
1617
+ const updated = new Set(tags || []);
1618
+ for (const tag of updated) {
1619
+ const next = applyTagUpdate(this.tagManifestFor(tag), durations, now);
1620
+ await this.revalidatedTagsMap.set(
1621
+ tag,
1622
+ persistableTagManifest(next, now)
1623
+ );
1624
+ }
1452
1625
  if (this.redisGetDeduplication && this.inMemoryCachingTime > 0) {
1453
- for (const key of keysToDelete) {
1454
- this.inMemoryDeduplicationCache.delete(key);
1626
+ for (const [key, sharedTags] of this.sharedTagsMap.entries()) {
1627
+ if (sharedTags.some((tag) => updated.has(tag))) {
1628
+ await this.inMemoryDeduplicationCache.delete(key, true);
1629
+ }
1455
1630
  }
1456
1631
  }
1457
- const deleteTagsOperation = this.sharedTagsMap.delete(cacheKeys);
1458
- await deleteTagsOperation;
1459
1632
  } catch (error) {
1460
1633
  console.error(
1461
1634
  "RedisCacheComponentsHandler.updateTags() Error occurred while updating tags. The original error was:",
@@ -1499,6 +1672,8 @@ var index_default = CachedHandler;
1499
1672
  bufferAndMapReviver,
1500
1673
  getRedisCacheComponentsHandler,
1501
1674
  jsonCacheValueSerializer,
1502
- redisCacheHandler
1675
+ redisCacheHandler,
1676
+ resolveCacheEntryTtlSeconds,
1677
+ shouldDeferRedisConnection
1503
1678
  });
1504
1679
  //# sourceMappingURL=index.js.map