@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.mjs CHANGED
@@ -22,6 +22,12 @@ function debugVerbose(color, ...args) {
22
22
  }
23
23
  }
24
24
 
25
+ // src/utils/redisConnection.ts
26
+ var PHASE_PRODUCTION_BUILD = "phase-production-build";
27
+ function shouldDeferRedisConnection() {
28
+ return process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD;
29
+ }
30
+
25
31
  // src/SyncedMap.ts
26
32
  var SYNC_CHANNEL_SUFFIX = ":sync-channel:";
27
33
  var SyncedMap = class {
@@ -47,6 +53,10 @@ var SyncedMap = class {
47
53
  });
48
54
  }
49
55
  async setup() {
56
+ if (shouldDeferRedisConnection()) {
57
+ this.setupLockResolve();
58
+ return;
59
+ }
50
60
  let setupPromises = [];
51
61
  if (!this.customizedSync?.withoutRedisHashmap) {
52
62
  setupPromises.push(this.initialSync());
@@ -57,45 +67,54 @@ var SyncedMap = class {
57
67
  this.setupLockResolve();
58
68
  }
59
69
  async initialSync() {
60
- let cursor = 0;
61
- const hScanOptions = { COUNT: this.querySize };
70
+ let cursor = "0";
62
71
  try {
63
72
  do {
64
- const remoteItems = await redisErrorHandler(
65
- "SyncedMap.initialSync(), operation: hScan " + this.syncChannel + " " + this.keyPrefix + " " + this.redisKey + " " + cursor + " " + this.querySize,
66
- this.client.hScan(
73
+ const [nextCursor, tuples] = await redisErrorHandler(
74
+ "SyncedMap.initialSync(), operation: HSCAN " + this.keyPrefix + this.redisKey,
75
+ this.client.sendCommand([
76
+ "HSCAN",
67
77
  this.keyPrefix + this.redisKey,
68
78
  cursor,
69
- hScanOptions
70
- )
79
+ "COUNT",
80
+ String(this.querySize)
81
+ ])
71
82
  );
72
- for (const { field, value } of remoteItems.tuples) {
83
+ for (let i = 0; i < tuples.length; i += 2) {
84
+ const field = tuples[i];
73
85
  if (this.filterKeys(field)) {
74
- const parsedValue = JSON.parse(value);
75
- this.map.set(field, parsedValue);
86
+ this.map.set(field, JSON.parse(tuples[i + 1]));
76
87
  }
77
88
  }
78
- cursor = remoteItems.cursor;
79
- } while (cursor !== 0);
80
- await this.cleanupKeysNotInRedis();
89
+ cursor = nextCursor;
90
+ } while (cursor !== "0");
91
+ if (!this.customizedSync?.withoutOrphanCleanup) {
92
+ await this.cleanupKeysNotInRedis();
93
+ }
81
94
  } catch (error) {
82
95
  console.error("Error during initial sync:", error);
83
96
  throw error;
84
97
  }
85
98
  }
86
99
  async cleanupKeysNotInRedis() {
87
- let cursor = 0;
88
- const scanOptions = { COUNT: this.querySize, MATCH: `${this.keyPrefix}*` };
100
+ let cursor = "0";
89
101
  let remoteKeys = [];
90
102
  try {
91
103
  do {
92
- const remoteKeysPortion = await redisErrorHandler(
93
- "SyncedMap.cleanupKeysNotInRedis(), operation: scan " + this.keyPrefix,
94
- this.client.scan(cursor, scanOptions)
104
+ const [nextCursor, keys] = await redisErrorHandler(
105
+ "SyncedMap.cleanupKeysNotInRedis(), operation: SCAN " + this.keyPrefix,
106
+ this.client.sendCommand([
107
+ "SCAN",
108
+ cursor,
109
+ "MATCH",
110
+ `${this.keyPrefix}*`,
111
+ "COUNT",
112
+ String(this.querySize)
113
+ ])
95
114
  );
96
- remoteKeys = remoteKeys.concat(remoteKeysPortion.keys);
97
- cursor = remoteKeysPortion.cursor;
98
- } while (cursor !== 0);
115
+ remoteKeys = remoteKeys.concat(keys);
116
+ cursor = nextCursor;
117
+ } while (cursor !== "0");
99
118
  const remoteKeysSet = new Set(
100
119
  remoteKeys.map((key) => key.substring(this.keyPrefix.length))
101
120
  );
@@ -489,6 +508,37 @@ var jsonCacheValueSerializer = {
489
508
  }
490
509
  };
491
510
 
511
+ // src/utils/cacheTtl.ts
512
+ function resolveCacheEntryTtlSeconds(ctx, data, options) {
513
+ const expire = ctx.cacheControl?.expire;
514
+ if (Number.isFinite(expire) && expire > 0) {
515
+ return Math.max(1, Math.ceil(expire));
516
+ }
517
+ const revalidate = resolveRevalidateValue(ctx, data);
518
+ if (revalidate === false) {
519
+ return options.estimateExpireAge(options.defaultStaleAge);
520
+ }
521
+ if (revalidate && Number.isSafeInteger(revalidate) && revalidate > 0) {
522
+ return options.estimateExpireAge(revalidate);
523
+ }
524
+ return void 0;
525
+ }
526
+ function resolveRevalidateValue(ctx, data) {
527
+ if (data?.kind === "FETCH" && data.revalidate !== void 0) {
528
+ return data.revalidate;
529
+ }
530
+ if (ctx.revalidate !== void 0) {
531
+ return ctx.revalidate;
532
+ }
533
+ if (ctx.cacheControl?.revalidate !== void 0) {
534
+ return ctx.cacheControl.revalidate;
535
+ }
536
+ if (data?.revalidate !== void 0) {
537
+ return data.revalidate;
538
+ }
539
+ return void 0;
540
+ }
541
+
492
542
  // src/RedisStringsHandler.ts
493
543
  function redisErrorHandler(debugInfo, redisCommandResult) {
494
544
  const beforeTimestamp = performance.now();
@@ -566,6 +616,7 @@ var RedisStringsHandler = class {
566
616
  this.killContainerOnErrorThreshold = killContainerOnErrorThreshold;
567
617
  this.getTimeoutMs = getTimeoutMs;
568
618
  this.valueSerializer = valueSerializer;
619
+ this.redisConnectionDeferred = shouldDeferRedisConnection();
569
620
  try {
570
621
  this.client = createClient({
571
622
  url: redisUrl,
@@ -604,12 +655,14 @@ var RedisStringsHandler = class {
604
655
  }, 500);
605
656
  }
606
657
  });
607
- this.client.connect().then(() => {
608
- debug("green", "Redis client connected.");
609
- }).catch((error) => {
610
- console.error("Failed to connect Redis client:", error);
611
- throw error;
612
- });
658
+ if (!this.redisConnectionDeferred) {
659
+ this.client.connect().then(() => {
660
+ debug("green", "Redis client connected.");
661
+ }).catch((error) => {
662
+ console.error("Failed to connect Redis client:", error);
663
+ throw error;
664
+ });
665
+ }
613
666
  } catch (error) {
614
667
  console.error("Failed to initialize Redis client");
615
668
  throw error;
@@ -631,6 +684,7 @@ var RedisStringsHandler = class {
631
684
  database,
632
685
  querySize: revalidateTagQuerySize,
633
686
  filterKeys,
687
+ customizedSync: { withoutOrphanCleanup: true },
634
688
  resyncIntervalMs: avgResyncIntervalMs + avgResyncIntervalMs / 10 + Math.random() * (avgResyncIntervalMs / 10)
635
689
  });
636
690
  this.inMemoryDeduplicationCache = new SyncedMap({
@@ -700,11 +754,17 @@ var RedisStringsHandler = class {
700
754
  }
701
755
  this.clientReadyCalls = 0;
702
756
  if (!this.client.isReady) {
757
+ if (this.redisConnectionDeferred) {
758
+ return;
759
+ }
703
760
  throw new Error(
704
761
  "assertClientIsReady: Redis client is not ready yet or connection is lost."
705
762
  );
706
763
  }
707
764
  }
765
+ isClientUnavailable() {
766
+ return this.redisConnectionDeferred || !this.client.isReady;
767
+ }
708
768
  async get(key, ctx) {
709
769
  try {
710
770
  if (!SUPPORTED_GET_KINDS.includes(ctx.kind)) {
@@ -718,6 +778,9 @@ var RedisStringsHandler = class {
718
778
  }
719
779
  debug("green", "RedisStringsHandler.get() called with", key, ctx);
720
780
  await this.assertClientIsReady();
781
+ if (this.isClientUnavailable()) {
782
+ return null;
783
+ }
721
784
  const clientGet = this.redisGetDeduplication ? this.deduplicatedRedisGet(key) : this.redisGet;
722
785
  const redisGetOperation = clientGet(
723
786
  commandOptions({ signal: AbortSignal.timeout(this.getTimeoutMs) }),
@@ -851,6 +914,9 @@ var RedisStringsHandler = class {
851
914
  );
852
915
  }
853
916
  await this.assertClientIsReady();
917
+ if (this.isClientUnavailable()) {
918
+ return;
919
+ }
854
920
  if (data?.kind === "APP_PAGE" || data?.kind === "APP_ROUTE") {
855
921
  const tags = data.headers["x-next-cache-tags"]?.split(",");
856
922
  ctx.tags = [...ctx.tags || [], ...tags || []];
@@ -873,18 +939,14 @@ var RedisStringsHandler = class {
873
939
  serializedCacheEntry
874
940
  );
875
941
  }
876
- const revalidate = (
877
- // 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
878
- data?.kind === "FETCH" && data.revalidate || ctx.revalidate || ctx.cacheControl?.revalidate || // Legacy fallback: older Next.js versions attached `revalidate` directly
879
- // to the data payload. FETCH is already handled above, so this only
880
- // matters for those older, non-discriminated shapes.
881
- data?.revalidate
882
- );
883
- const expireAt = revalidate && Number.isSafeInteger(revalidate) && revalidate > 0 ? this.estimateExpireAge(revalidate) : this.estimateExpireAge(this.defaultStaleAge);
942
+ const expireAt = resolveCacheEntryTtlSeconds(ctx, data, {
943
+ estimateExpireAge: this.estimateExpireAge,
944
+ defaultStaleAge: this.defaultStaleAge
945
+ });
884
946
  const setOperation = redisErrorHandler(
885
947
  "RedisStringsHandler.set(), operation: set " + this.keyPrefix + " " + key,
886
948
  this.client.set(this.keyPrefix + key, serializedCacheEntry, {
887
- EX: expireAt
949
+ ...expireAt ? { EX: expireAt } : {}
888
950
  })
889
951
  );
890
952
  debug(
@@ -947,6 +1009,9 @@ var RedisStringsHandler = class {
947
1009
  );
948
1010
  const tags = new Set([tagOrTags || []].flat());
949
1011
  await this.assertClientIsReady();
1012
+ if (this.isClientUnavailable()) {
1013
+ return;
1014
+ }
950
1015
  const keysToDelete = /* @__PURE__ */ new Set();
951
1016
  for (const tag of tags) {
952
1017
  if (tag.startsWith(NEXT_CACHE_IMPLICIT_TAG_ID)) {
@@ -1085,6 +1150,88 @@ var CachedHandler = class {
1085
1150
 
1086
1151
  // src/CacheComponentsHandler.ts
1087
1152
  import { commandOptions as commandOptions2, createClient as createClient2 } from "redis";
1153
+
1154
+ // src/utils/compareAndUnlink.ts
1155
+ var COMPARE_AND_UNLINK = `
1156
+ if redis.call('GET', KEYS[1]) == ARGV[1] then
1157
+ return redis.call('UNLINK', KEYS[1])
1158
+ end
1159
+ return 0
1160
+ `;
1161
+ async function compareAndUnlink(client, key, expected) {
1162
+ const deleted = await client.eval(COMPARE_AND_UNLINK, {
1163
+ keys: [key],
1164
+ arguments: [expected]
1165
+ });
1166
+ return deleted === 1 || deleted === 1n;
1167
+ }
1168
+
1169
+ // src/utils/tagRevalidation.ts
1170
+ function normalizeTagManifest(stored, now = Date.now()) {
1171
+ if (typeof stored === "number" && Number.isFinite(stored)) {
1172
+ return { expired: Math.min(stored, now) };
1173
+ }
1174
+ if (stored && typeof stored === "object") {
1175
+ const rec = stored;
1176
+ const stale = typeof rec.stale === "number" && Number.isFinite(rec.stale) ? rec.stale : typeof rec.last === "number" && Number.isFinite(rec.last) ? rec.last : void 0;
1177
+ const expired = typeof rec.expired === "number" && Number.isFinite(rec.expired) ? rec.expired : typeof rec.pending === "number" && Number.isFinite(rec.pending) ? rec.pending : void 0;
1178
+ return {
1179
+ ...stale !== void 0 ? { stale } : {},
1180
+ ...expired !== void 0 ? { expired } : {}
1181
+ };
1182
+ }
1183
+ return {};
1184
+ }
1185
+ function applyTagUpdate(existing, durations, now) {
1186
+ if (durations) {
1187
+ const updates = { ...existing };
1188
+ updates.stale = now;
1189
+ if (durations.expire !== void 0) {
1190
+ updates.expired = now + durations.expire * 1e3;
1191
+ }
1192
+ return updates;
1193
+ }
1194
+ return {
1195
+ ...existing,
1196
+ expired: now
1197
+ };
1198
+ }
1199
+ function persistableTagManifest(entry, now) {
1200
+ if (typeof entry.expired === "number" && entry.expired <= now && (entry.stale === void 0 || entry.stale <= entry.expired)) {
1201
+ return entry.expired;
1202
+ }
1203
+ return entry;
1204
+ }
1205
+ function areTagsExpired(tags, entryTimestamp, now, getEntry) {
1206
+ for (const tag of tags) {
1207
+ const expiredAt = getEntry(tag)?.expired;
1208
+ if (typeof expiredAt === "number" && expiredAt <= now && expiredAt > entryTimestamp) {
1209
+ return true;
1210
+ }
1211
+ }
1212
+ return false;
1213
+ }
1214
+ function areTagsStale(tags, entryTimestamp, getEntry) {
1215
+ for (const tag of tags) {
1216
+ const staleAt = getEntry(tag)?.stale ?? 0;
1217
+ if (typeof staleAt === "number" && staleAt > entryTimestamp) {
1218
+ return true;
1219
+ }
1220
+ }
1221
+ return false;
1222
+ }
1223
+ function maxExpiredTimestamp(tags, getEntry) {
1224
+ let max = 0;
1225
+ for (const tag of tags) {
1226
+ const expired = getEntry(tag)?.expired || 0;
1227
+ if (expired > max) {
1228
+ max = expired;
1229
+ }
1230
+ }
1231
+ return max;
1232
+ }
1233
+
1234
+ // src/CacheComponentsHandler.ts
1088
1235
  var REVALIDATED_TAGS_KEY2 = "__cacheComponents_revalidated_tags__";
1089
1236
  var SHARED_TAGS_KEY = "__cacheComponents_sharedTags__";
1090
1237
  var killContainerOnErrorCount2 = 0;
@@ -1142,6 +1289,7 @@ var RedisCacheComponentsHandler = class {
1142
1289
  this.getTimeoutMs = getTimeoutMs;
1143
1290
  this.redisGetDeduplication = redisGetDeduplication;
1144
1291
  this.inMemoryCachingTime = inMemoryCachingTime;
1292
+ this.redisConnectionDeferred = shouldDeferRedisConnection();
1145
1293
  this.client = createClient2({
1146
1294
  url: redisUrl,
1147
1295
  pingInterval: 1e4,
@@ -1177,18 +1325,20 @@ var RedisCacheComponentsHandler = class {
1177
1325
  }, 500);
1178
1326
  }
1179
1327
  });
1180
- this.client.connect().then(() => {
1181
- debug("green", "RedisCacheComponentsHandler client connected.");
1182
- }).catch(() => {
1183
- this.client.connect().catch((error) => {
1184
- console.error(
1185
- "Failed to connect RedisCacheComponentsHandler client:",
1186
- error
1187
- );
1188
- this.client.disconnect();
1189
- throw error;
1328
+ if (!this.redisConnectionDeferred) {
1329
+ this.client.connect().then(() => {
1330
+ debug("green", "RedisCacheComponentsHandler client connected.");
1331
+ }).catch(() => {
1332
+ this.client.connect().catch((error) => {
1333
+ console.error(
1334
+ "Failed to connect RedisCacheComponentsHandler client:",
1335
+ error
1336
+ );
1337
+ this.client.disconnect();
1338
+ throw error;
1339
+ });
1190
1340
  });
1191
- });
1341
+ }
1192
1342
  const filterKeys = (key) => key !== REVALIDATED_TAGS_KEY2 && key !== SHARED_TAGS_KEY;
1193
1343
  this.revalidatedTagsMap = new SyncedMap({
1194
1344
  client: this.client,
@@ -1197,6 +1347,7 @@ var RedisCacheComponentsHandler = class {
1197
1347
  database,
1198
1348
  querySize: revalidateTagQuerySize,
1199
1349
  filterKeys,
1350
+ customizedSync: { withoutOrphanCleanup: true },
1200
1351
  resyncIntervalMs: avgResyncIntervalMs + avgResyncIntervalMs / 10 + Math.random() * (avgResyncIntervalMs / 10)
1201
1352
  });
1202
1353
  this.sharedTagsMap = new SyncedMap({
@@ -1234,6 +1385,13 @@ var RedisCacheComponentsHandler = class {
1234
1385
  }
1235
1386
  }
1236
1387
  async assertClientIsReady() {
1388
+ if (this.redisConnectionDeferred) {
1389
+ await Promise.all([
1390
+ this.revalidatedTagsMap.waitUntilReady(),
1391
+ this.sharedTagsMap.waitUntilReady()
1392
+ ]);
1393
+ return;
1394
+ }
1237
1395
  if (!this.client.isReady && !this.client.isOpen) {
1238
1396
  await this.client.connect().catch((error) => {
1239
1397
  console.error(
@@ -1248,57 +1406,76 @@ var RedisCacheComponentsHandler = class {
1248
1406
  this.sharedTagsMap.waitUntilReady()
1249
1407
  ]);
1250
1408
  }
1251
- async computeMaxRevalidation(tags) {
1252
- let max = 0;
1253
- for (const tag of tags) {
1254
- const ts = this.revalidatedTagsMap.get(tag);
1255
- if (ts && ts > max) {
1256
- max = ts;
1257
- }
1258
- }
1259
- return max;
1409
+ isClientUnavailable() {
1410
+ return this.redisConnectionDeferred || !this.client.isReady;
1411
+ }
1412
+ tagManifestFor(tag, now = Date.now()) {
1413
+ return normalizeTagManifest(this.revalidatedTagsMap.get(tag), now);
1260
1414
  }
1261
- async get(cacheKey, softTags) {
1415
+ async get(cacheKey, _softTags) {
1416
+ void _softTags;
1262
1417
  const redisKey = `${this.keyPrefix}${cacheKey}`;
1263
1418
  try {
1264
1419
  await this.assertClientIsReady();
1265
- const clientGet = this.redisGetDeduplication ? this.deduplicatedRedisGet(cacheKey) : this.redisGet;
1266
- const serialized = await redisErrorHandler(
1267
- "RedisCacheComponentsHandler.get(), operation: get" + (this.redisGetDeduplication ? " deduplicated" : "") + " " + this.getTimeoutMs + "ms " + redisKey,
1268
- clientGet(
1420
+ if (this.isClientUnavailable()) {
1421
+ return void 0;
1422
+ }
1423
+ const readSerialized = (useDedup) => redisErrorHandler(
1424
+ "RedisCacheComponentsHandler.get(), operation: get" + (useDedup ? " deduplicated" : "") + " " + this.getTimeoutMs + "ms " + redisKey,
1425
+ (useDedup ? this.deduplicatedRedisGet(cacheKey) : this.redisGet)(
1269
1426
  commandOptions2({ signal: AbortSignal.timeout(this.getTimeoutMs) }),
1270
1427
  redisKey
1271
1428
  )
1272
1429
  );
1273
- if (!serialized) {
1274
- return void 0;
1275
- }
1276
- const stored = JSON.parse(serialized);
1277
- const now = Date.now();
1278
- const expiryTime = stored.timestamp + stored.expire * 1e3;
1279
- if (Number.isFinite(stored.expire) && stored.expire > 0 && now > expiryTime) {
1280
- await this.client.unlink(redisKey).catch(() => {
1281
- });
1282
- await this.sharedTagsMap.delete(cacheKey).catch(() => {
1283
- });
1284
- return void 0;
1285
- }
1286
- const maxRevalidation = await this.computeMaxRevalidation([
1287
- ...stored.tags || [],
1288
- ...softTags || []
1289
- ]);
1290
- if (maxRevalidation > 0 && maxRevalidation > stored.timestamp) {
1291
- await this.client.unlink(redisKey).catch(() => {
1292
- });
1293
- await this.sharedTagsMap.delete(cacheKey).catch(() => {
1294
- });
1295
- return void 0;
1430
+ let usedDedup = this.redisGetDeduplication;
1431
+ let serialized = await readSerialized(usedDedup);
1432
+ for (let attempt = 0; attempt < 2; attempt++) {
1433
+ if (!serialized) {
1434
+ return void 0;
1435
+ }
1436
+ const stored = JSON.parse(serialized);
1437
+ const now = Date.now();
1438
+ const tags = stored.tags || [];
1439
+ const expiryTime = stored.timestamp + stored.expire * 1e3;
1440
+ const ttlExpired = Number.isFinite(stored.expire) && stored.expire > 0 && now > expiryTime;
1441
+ const tagsExpired = areTagsExpired(
1442
+ tags,
1443
+ stored.timestamp,
1444
+ now,
1445
+ (tag) => this.tagManifestFor(tag, now)
1446
+ );
1447
+ if (ttlExpired || tagsExpired) {
1448
+ if (usedDedup) {
1449
+ usedDedup = false;
1450
+ serialized = await readSerialized(false);
1451
+ continue;
1452
+ }
1453
+ const dropped = await compareAndUnlink(
1454
+ this.client,
1455
+ redisKey,
1456
+ serialized
1457
+ );
1458
+ if (dropped) {
1459
+ await this.sharedTagsMap.delete(cacheKey).catch(() => {
1460
+ });
1461
+ }
1462
+ return void 0;
1463
+ }
1464
+ const valueBuffer = typeof stored.value === "string" ? new Uint8Array(Buffer.from(stored.value, "base64")) : stored.value;
1465
+ const entry = {
1466
+ ...stored,
1467
+ value: bufferToReadableStream(valueBuffer)
1468
+ };
1469
+ if (areTagsStale(
1470
+ tags,
1471
+ stored.timestamp,
1472
+ (tag) => this.tagManifestFor(tag, now)
1473
+ )) {
1474
+ entry.revalidate = -1;
1475
+ }
1476
+ return entry;
1296
1477
  }
1297
- const valueBuffer = typeof stored.value === "string" ? new Uint8Array(Buffer.from(stored.value, "base64")) : stored.value;
1298
- return {
1299
- ...stored,
1300
- value: bufferToReadableStream(valueBuffer)
1301
- };
1478
+ return void 0;
1302
1479
  } catch (error) {
1303
1480
  console.error(
1304
1481
  "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:",
@@ -1311,6 +1488,9 @@ var RedisCacheComponentsHandler = class {
1311
1488
  async set(cacheKey, pendingEntry) {
1312
1489
  try {
1313
1490
  await this.assertClientIsReady();
1491
+ if (this.isClientUnavailable()) {
1492
+ return;
1493
+ }
1314
1494
  const entry = await pendingEntry;
1315
1495
  const [storeStream] = entry.value.tee();
1316
1496
  const buffer = await streamToBuffer(storeStream);
@@ -1374,7 +1554,7 @@ var RedisCacheComponentsHandler = class {
1374
1554
  async getExpiration(tags) {
1375
1555
  try {
1376
1556
  await this.assertClientIsReady();
1377
- return this.computeMaxRevalidation(tags || []);
1557
+ return maxExpiredTimestamp(tags || [], (tag) => this.tagManifestFor(tag));
1378
1558
  } catch (error) {
1379
1559
  console.error(
1380
1560
  "RedisCacheComponentsHandler.getExpiration() Error occurred while getting expiration for tags. The original error was:",
@@ -1383,37 +1563,28 @@ var RedisCacheComponentsHandler = class {
1383
1563
  return 0;
1384
1564
  }
1385
1565
  }
1386
- async updateTags(tags, _durations) {
1566
+ async updateTags(tags, durations) {
1387
1567
  try {
1388
- void _durations;
1389
1568
  await this.assertClientIsReady();
1390
- const now = Date.now();
1391
- const tagsSet = new Set(tags || []);
1392
- for (const tag of tagsSet) {
1393
- await this.revalidatedTagsMap.set(tag, now);
1394
- }
1395
- const keysToDelete = /* @__PURE__ */ new Set();
1396
- for (const [key, storedTags] of this.sharedTagsMap.entries()) {
1397
- if (storedTags.some((tag) => tagsSet.has(tag))) {
1398
- keysToDelete.add(key);
1399
- }
1400
- }
1401
- if (keysToDelete.size === 0) {
1569
+ if (this.isClientUnavailable()) {
1402
1570
  return;
1403
1571
  }
1404
- const cacheKeys = Array.from(keysToDelete);
1405
- const fullRedisKeys = cacheKeys.map((key) => `${this.keyPrefix}${key}`);
1406
- await redisErrorHandler(
1407
- "RedisCacheComponentsHandler.updateTags(), operation: unlink",
1408
- this.client.unlink(fullRedisKeys)
1409
- );
1572
+ const now = Date.now();
1573
+ const updated = new Set(tags || []);
1574
+ for (const tag of updated) {
1575
+ const next = applyTagUpdate(this.tagManifestFor(tag), durations, now);
1576
+ await this.revalidatedTagsMap.set(
1577
+ tag,
1578
+ persistableTagManifest(next, now)
1579
+ );
1580
+ }
1410
1581
  if (this.redisGetDeduplication && this.inMemoryCachingTime > 0) {
1411
- for (const key of keysToDelete) {
1412
- this.inMemoryDeduplicationCache.delete(key);
1582
+ for (const [key, sharedTags] of this.sharedTagsMap.entries()) {
1583
+ if (sharedTags.some((tag) => updated.has(tag))) {
1584
+ await this.inMemoryDeduplicationCache.delete(key, true);
1585
+ }
1413
1586
  }
1414
1587
  }
1415
- const deleteTagsOperation = this.sharedTagsMap.delete(cacheKeys);
1416
- await deleteTagsOperation;
1417
1588
  } catch (error) {
1418
1589
  console.error(
1419
1590
  "RedisCacheComponentsHandler.updateTags() Error occurred while updating tags. The original error was:",
@@ -1457,6 +1628,8 @@ export {
1457
1628
  index_default as default,
1458
1629
  getRedisCacheComponentsHandler,
1459
1630
  jsonCacheValueSerializer,
1460
- redisCacheHandler
1631
+ redisCacheHandler,
1632
+ resolveCacheEntryTtlSeconds,
1633
+ shouldDeferRedisConnection
1461
1634
  };
1462
1635
  //# sourceMappingURL=index.mjs.map