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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/dist/index.js +27 -20
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +27 -20
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +6 -1
  6. package/.cursor/skills/testing/SKILL.md +0 -121
  7. package/.github/workflows/ci.yml +0 -309
  8. package/.github/workflows/pages.yml +0 -38
  9. package/.github/workflows/release.yml +0 -58
  10. package/.prettierrc.json +0 -6
  11. package/.vscode/settings.json +0 -10
  12. package/ARCHITECTURE.md +0 -470
  13. package/commitlint.config.cjs +0 -4
  14. package/docker/redis.conf +0 -1
  15. package/docs/index.html +0 -140
  16. package/docs/robots.txt +0 -4
  17. package/docs/sitemap.xml +0 -8
  18. package/docs/styles.css +0 -141
  19. package/eslint.config.mjs +0 -26
  20. package/hooks/commit-msg +0 -10
  21. package/hooks/pre-commit +0 -11
  22. package/playwright.config.ts +0 -18
  23. package/release.config.cjs +0 -12
  24. package/scripts/prepare.sh +0 -7
  25. package/scripts/setup-git.sh +0 -8
  26. package/scripts/setup-repo-name.sh +0 -17
  27. package/scripts/vitest-run-staged.cjs +0 -44
  28. package/src/CacheComponentsHandler.ts +0 -597
  29. package/src/CachedHandler.ts +0 -53
  30. package/src/DeduplicatedRequestHandler.ts +0 -110
  31. package/src/RedisStringsHandler.ts +0 -949
  32. package/src/SyncedMap.ts +0 -496
  33. package/src/ZodHandler.ts +0 -45
  34. package/src/index.ts +0 -23
  35. package/src/serializer.ts +0 -59
  36. package/src/utils/cacheTtl.ts +0 -71
  37. package/src/utils/compareAndUnlink.ts +0 -29
  38. package/src/utils/debug.ts +0 -30
  39. package/src/utils/json.ts +0 -49
  40. package/src/utils/prefix.ts +0 -44
  41. package/src/utils/redisConnection.ts +0 -17
  42. package/src/utils/tagRevalidation.ts +0 -162
  43. package/test/README.md +0 -194
  44. package/test/nextjs-test-projects/next-app-15-4-11/README.md +0 -36
  45. package/test/nextjs-test-projects/next-app-15-4-11/eslint.config.mjs +0 -16
  46. package/test/nextjs-test-projects/next-app-15-4-11/next.config.js +0 -9
  47. package/test/nextjs-test-projects/next-app-15-4-11/package-lock.json +0 -5969
  48. package/test/nextjs-test-projects/next-app-15-4-11/package.json +0 -29
  49. package/test/nextjs-test-projects/next-app-15-4-11/pnpm-lock.yaml +0 -3726
  50. package/test/nextjs-test-projects/next-app-15-4-11/postcss.config.mjs +0 -5
  51. package/test/nextjs-test-projects/next-app-15-4-11/public/file.svg +0 -1
  52. package/test/nextjs-test-projects/next-app-15-4-11/public/globe.svg +0 -1
  53. package/test/nextjs-test-projects/next-app-15-4-11/public/next.svg +0 -1
  54. package/test/nextjs-test-projects/next-app-15-4-11/public/vercel.svg +0 -1
  55. package/test/nextjs-test-projects/next-app-15-4-11/public/window.svg +0 -1
  56. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/cached-static-fetch/route.ts +0 -18
  57. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  58. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidatePath/route.ts +0 -15
  59. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidateTag/route.ts +0 -15
  60. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidated-fetch/route.ts +0 -17
  61. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/uncached-fetch/route.ts +0 -15
  62. package/test/nextjs-test-projects/next-app-15-4-11/src/app/globals.css +0 -26
  63. package/test/nextjs-test-projects/next-app-15-4-11/src/app/layout.tsx +0 -59
  64. package/test/nextjs-test-projects/next-app-15-4-11/src/app/page.tsx +0 -755
  65. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  66. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  67. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  68. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  69. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  70. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  71. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  72. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  73. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  74. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  75. package/test/nextjs-test-projects/next-app-15-4-11/src/app/revalidation-interface.tsx +0 -267
  76. package/test/nextjs-test-projects/next-app-15-4-11/tsconfig.json +0 -27
  77. package/test/nextjs-test-projects/next-app-16-0-11/README.md +0 -36
  78. package/test/nextjs-test-projects/next-app-16-0-11/eslint.config.mjs +0 -18
  79. package/test/nextjs-test-projects/next-app-16-0-11/next.config.ts +0 -10
  80. package/test/nextjs-test-projects/next-app-16-0-11/package.json +0 -28
  81. package/test/nextjs-test-projects/next-app-16-0-11/pnpm-lock.yaml +0 -4163
  82. package/test/nextjs-test-projects/next-app-16-0-11/postcss.config.mjs +0 -7
  83. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/cached-static-fetch/route.ts +0 -18
  84. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  85. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidatePath/route.ts +0 -15
  86. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidateTag/route.ts +0 -20
  87. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidated-fetch/route.ts +0 -17
  88. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/uncached-fetch/route.ts +0 -15
  89. package/test/nextjs-test-projects/next-app-16-0-11/src/app/favicon.ico +0 -0
  90. package/test/nextjs-test-projects/next-app-16-0-11/src/app/globals.css +0 -26
  91. package/test/nextjs-test-projects/next-app-16-0-11/src/app/layout.tsx +0 -59
  92. package/test/nextjs-test-projects/next-app-16-0-11/src/app/page.tsx +0 -755
  93. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  94. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  95. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  96. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  97. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  98. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  99. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  100. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  101. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  102. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  103. package/test/nextjs-test-projects/next-app-16-0-11/src/app/revalidation-interface.tsx +0 -267
  104. package/test/nextjs-test-projects/next-app-16-0-11/src/app/update-tag-test/page.tsx +0 -25
  105. package/test/nextjs-test-projects/next-app-16-0-11/tsconfig.json +0 -34
  106. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/README.md +0 -36
  107. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/cache-handler.js +0 -3
  108. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/eslint.config.mjs +0 -18
  109. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/next.config.ts +0 -15
  110. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/package.json +0 -28
  111. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/pnpm-lock.yaml +0 -4206
  112. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/postcss.config.mjs +0 -7
  113. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/file.svg +0 -1
  114. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/globe.svg +0 -1
  115. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/next.svg +0 -1
  116. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/vercel.svg +0 -1
  117. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/window.svg +0 -1
  118. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  119. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  120. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  121. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  122. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  123. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  124. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  125. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  126. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/page.tsx +0 -112
  127. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  128. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  129. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  130. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  131. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  132. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  133. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/favicon.ico +0 -0
  134. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/globals.css +0 -26
  135. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/layout.tsx +0 -57
  136. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/page.tsx +0 -755
  137. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/revalidation-interface.tsx +0 -267
  138. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/update-tag-test/page.tsx +0 -22
  139. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/tsconfig.json +0 -34
  140. package/test/nextjs-test-projects/next-app-16-2-6/README.md +0 -36
  141. package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +0 -18
  142. package/test/nextjs-test-projects/next-app-16-2-6/next.config.ts +0 -7
  143. package/test/nextjs-test-projects/next-app-16-2-6/package.json +0 -28
  144. package/test/nextjs-test-projects/next-app-16-2-6/pnpm-lock.yaml +0 -4165
  145. package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +0 -7
  146. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/cached-static-fetch/route.ts +0 -18
  147. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  148. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidatePath/route.ts +0 -15
  149. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidateTag/route.ts +0 -20
  150. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidated-fetch/route.ts +0 -17
  151. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/uncached-fetch/route.ts +0 -15
  152. package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
  153. package/test/nextjs-test-projects/next-app-16-2-6/src/app/globals.css +0 -26
  154. package/test/nextjs-test-projects/next-app-16-2-6/src/app/layout.tsx +0 -59
  155. package/test/nextjs-test-projects/next-app-16-2-6/src/app/page.tsx +0 -755
  156. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  157. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  158. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  159. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  160. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  161. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  162. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  163. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  164. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  165. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  166. package/test/nextjs-test-projects/next-app-16-2-6/src/app/revalidation-interface.tsx +0 -267
  167. package/test/nextjs-test-projects/next-app-16-2-6/src/app/update-tag-test/page.tsx +0 -25
  168. package/test/nextjs-test-projects/next-app-16-2-6/tsconfig.json +0 -34
  169. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/README.md +0 -36
  170. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +0 -3
  171. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/eslint.config.mjs +0 -18
  172. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +0 -15
  173. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/package.json +0 -28
  174. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/pnpm-lock.yaml +0 -4165
  175. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/postcss.config.mjs +0 -7
  176. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/file.svg +0 -1
  177. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/globe.svg +0 -1
  178. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/next.svg +0 -1
  179. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/vercel.svg +0 -1
  180. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/window.svg +0 -1
  181. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  182. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  183. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  184. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  185. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  186. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  187. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  188. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  189. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +0 -112
  190. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  191. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  192. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  193. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  194. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  195. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  196. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/favicon.ico +0 -0
  197. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/globals.css +0 -26
  198. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/layout.tsx +0 -57
  199. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/page.tsx +0 -755
  200. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/revalidation-interface.tsx +0 -267
  201. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +0 -22
  202. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/tsconfig.json +0 -34
  203. package/test/nextjs-test-projects/next-app-16-3-0/README.md +0 -36
  204. package/test/nextjs-test-projects/next-app-16-3-0/eslint.config.mjs +0 -18
  205. package/test/nextjs-test-projects/next-app-16-3-0/next.config.ts +0 -7
  206. package/test/nextjs-test-projects/next-app-16-3-0/package.json +0 -28
  207. package/test/nextjs-test-projects/next-app-16-3-0/pnpm-lock.yaml +0 -4284
  208. package/test/nextjs-test-projects/next-app-16-3-0/postcss.config.mjs +0 -7
  209. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/cached-static-fetch/route.ts +0 -18
  210. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  211. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidatePath/route.ts +0 -15
  212. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidateTag/route.ts +0 -20
  213. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidated-fetch/route.ts +0 -17
  214. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/uncached-fetch/route.ts +0 -15
  215. package/test/nextjs-test-projects/next-app-16-3-0/src/app/favicon.ico +0 -0
  216. package/test/nextjs-test-projects/next-app-16-3-0/src/app/globals.css +0 -26
  217. package/test/nextjs-test-projects/next-app-16-3-0/src/app/layout.tsx +0 -59
  218. package/test/nextjs-test-projects/next-app-16-3-0/src/app/page.tsx +0 -755
  219. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  220. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  221. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  222. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  223. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  224. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  225. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  226. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  227. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  228. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  229. package/test/nextjs-test-projects/next-app-16-3-0/src/app/revalidation-interface.tsx +0 -267
  230. package/test/nextjs-test-projects/next-app-16-3-0/src/app/update-tag-test/page.tsx +0 -25
  231. package/test/nextjs-test-projects/next-app-16-3-0/tsconfig.json +0 -34
  232. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/README.md +0 -36
  233. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/cache-handler.js +0 -3
  234. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/eslint.config.mjs +0 -18
  235. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/next.config.ts +0 -15
  236. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/package.json +0 -28
  237. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/pnpm-lock.yaml +0 -4284
  238. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/postcss.config.mjs +0 -7
  239. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/file.svg +0 -1
  240. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/globe.svg +0 -1
  241. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/next.svg +0 -1
  242. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/vercel.svg +0 -1
  243. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/window.svg +0 -1
  244. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  245. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  246. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  247. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  248. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  249. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  250. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  251. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  252. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/page.tsx +0 -112
  253. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  254. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  255. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  256. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  257. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  258. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  259. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/favicon.ico +0 -0
  260. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/globals.css +0 -26
  261. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/layout.tsx +0 -57
  262. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/page.tsx +0 -755
  263. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/revalidation-interface.tsx +0 -267
  264. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/update-tag-test/page.tsx +0 -22
  265. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/tsconfig.json +0 -34
  266. package/test/nextjs-test-projects/next-app-customized/README.md +0 -36
  267. package/test/nextjs-test-projects/next-app-customized/customized-cache-handler.js +0 -34
  268. package/test/nextjs-test-projects/next-app-customized/eslint.config.mjs +0 -16
  269. package/test/nextjs-test-projects/next-app-customized/next.config.js +0 -6
  270. package/test/nextjs-test-projects/next-pages-16-2-6/README.md +0 -16
  271. package/test/nextjs-test-projects/next-pages-16-2-6/eslint.config.mjs +0 -18
  272. package/test/nextjs-test-projects/next-pages-16-2-6/next.config.ts +0 -7
  273. package/test/nextjs-test-projects/next-pages-16-2-6/package.json +0 -26
  274. package/test/nextjs-test-projects/next-pages-16-2-6/pnpm-lock.yaml +0 -3896
  275. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/api/revalidate.ts +0 -24
  276. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/index.tsx +0 -11
  277. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/isr/[slug].tsx +0 -49
  278. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/static-forever.tsx +0 -20
  279. package/test/nextjs-test-projects/next-pages-16-2-6/tsconfig.json +0 -34
  280. package/test/nextjs-test-projects/next-pages-16-3-0/README.md +0 -16
  281. package/test/nextjs-test-projects/next-pages-16-3-0/eslint.config.mjs +0 -18
  282. package/test/nextjs-test-projects/next-pages-16-3-0/next.config.ts +0 -7
  283. package/test/nextjs-test-projects/next-pages-16-3-0/package.json +0 -26
  284. package/test/nextjs-test-projects/next-pages-16-3-0/pnpm-lock.yaml +0 -3939
  285. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/api/revalidate.ts +0 -24
  286. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/index.tsx +0 -11
  287. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/isr/[slug].tsx +0 -49
  288. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/static-forever.tsx +0 -20
  289. package/test/nextjs-test-projects/next-pages-16-3-0/tsconfig.json +0 -34
  290. package/test/playwright/cache-lab.spec.ts +0 -232
  291. package/test/playwright/update-tag.spec.ts +0 -33
  292. package/test/vitest/integration/build-id-prefix.integration.test.ts +0 -102
  293. package/test/vitest/integration/cache-components/cache-components.integration.test.ts +0 -389
  294. package/test/vitest/integration/cache-components/redis-kill-reconnect.test.ts +0 -65
  295. package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +0 -59
  296. package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +0 -100
  297. package/test/vitest/integration/cache-components/scripts/redis-kill-reconnect.ts +0 -243
  298. package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +0 -113
  299. package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +0 -379
  300. package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +0 -155
  301. package/test/vitest/integration/nextjs-cache-handler.integration.test.ts +0 -959
  302. package/test/vitest/integration/pages-router.integration.test.ts +0 -430
  303. package/test/vitest/unit/SyncedMap-orphan-cleanup.test.ts +0 -111
  304. package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +0 -154
  305. package/test/vitest/unit/cache-components-update-tags.test.ts +0 -350
  306. package/test/vitest/unit/cache-ttl.test.ts +0 -67
  307. package/test/vitest/unit/compare-and-unlink.test.ts +0 -22
  308. package/test/vitest/unit/index.test.ts +0 -190
  309. package/test/vitest/unit/pages-router-kinds.test.ts +0 -303
  310. package/test/vitest/unit/reconnect-socket-already-opened.test.ts +0 -117
  311. package/test/vitest/unit/redis-connection.test.ts +0 -39
  312. package/test/vitest/unit/serializer.test.ts +0 -182
  313. package/test/vitest/unit/tag-revalidation.test.ts +0 -178
  314. package/test/vitest/unit/utils/prefix.test.ts +0 -115
  315. package/tsconfig.json +0 -18
  316. package/tsup.config.ts +0 -14
  317. package/vite.config.ts +0 -34
  318. package/vitest.cache-components.config.ts +0 -16
package/src/SyncedMap.ts DELETED
@@ -1,496 +0,0 @@
1
- // SyncedMap.ts
2
- import { Client, redisErrorHandler } from './RedisStringsHandler';
3
- import { debugVerbose, debug } from './utils/debug';
4
- import { shouldDeferRedisConnection } from './utils/redisConnection';
5
-
6
- type CustomizedSync = {
7
- withoutRedisHashmap?: boolean;
8
- withoutSetSync?: boolean;
9
- /**
10
- * Skip SCAN-based orphan cleanup. Required for maps whose keys are not Redis
11
- * string keys (tag manifests). Otherwise startup/resync HDELs every tag.
12
- */
13
- withoutOrphanCleanup?: boolean;
14
- };
15
-
16
- type SyncedMapOptions = {
17
- client: Client;
18
- keyPrefix: string;
19
- redisKey: string; // Redis Hash key
20
- database: number;
21
- querySize: number;
22
- filterKeys: (key: string) => boolean;
23
- resyncIntervalMs?: number;
24
- customizedSync?: CustomizedSync;
25
- };
26
-
27
- export type SyncMessage<V> = {
28
- type: 'insert' | 'delete';
29
- key?: string;
30
- value?: V;
31
- keys?: string[];
32
- };
33
-
34
- const SYNC_CHANNEL_SUFFIX = ':sync-channel:';
35
- export class SyncedMap<V> {
36
- private client: Client;
37
- private subscriberClient: Client;
38
- private map: Map<string, V>;
39
- private keyPrefix: string;
40
- private syncChannel: string;
41
- private redisKey: string;
42
- private database: number;
43
- private querySize: number;
44
- private filterKeys: (key: string) => boolean;
45
- private resyncIntervalMs?: number;
46
- private customizedSync?: CustomizedSync;
47
-
48
- private setupLock: Promise<void>;
49
- private setupLockResolve!: () => void;
50
- private isReconnecting = false;
51
-
52
- constructor(options: SyncedMapOptions) {
53
- this.client = options.client;
54
- this.keyPrefix = options.keyPrefix;
55
- this.redisKey = options.redisKey;
56
- this.syncChannel = `${options.keyPrefix}${SYNC_CHANNEL_SUFFIX}${options.redisKey}`;
57
- this.database = options.database;
58
- this.querySize = options.querySize;
59
- this.filterKeys = options.filterKeys;
60
- this.resyncIntervalMs = options.resyncIntervalMs;
61
- this.customizedSync = options.customizedSync;
62
-
63
- this.map = new Map<string, V>();
64
- this.subscriberClient = this.client.duplicate();
65
- this.setupLock = new Promise<void>((resolve) => {
66
- this.setupLockResolve = resolve;
67
- });
68
-
69
- this.setup().catch((error) => {
70
- console.error('Failed to setup SyncedMap:', error);
71
- throw error;
72
- });
73
- }
74
-
75
- private async setup() {
76
- if (shouldDeferRedisConnection()) {
77
- this.setupLockResolve();
78
- return;
79
- }
80
-
81
- let setupPromises: Promise<void>[] = [];
82
- if (!this.customizedSync?.withoutRedisHashmap) {
83
- setupPromises.push(this.initialSync());
84
- this.setupPeriodicResync();
85
- }
86
- setupPromises.push(this.setupPubSub());
87
- await Promise.all(setupPromises);
88
- this.setupLockResolve();
89
- }
90
-
91
- private async initialSync() {
92
- let cursor = 0;
93
- const hScanOptions = { COUNT: this.querySize };
94
-
95
- try {
96
- do {
97
- const remoteItems = await redisErrorHandler(
98
- 'SyncedMap.initialSync(), operation: hScan ' +
99
- this.syncChannel +
100
- ' ' +
101
- this.keyPrefix +
102
- ' ' +
103
- this.redisKey +
104
- ' ' +
105
- cursor +
106
- ' ' +
107
- this.querySize,
108
- this.client.hScan(
109
- this.keyPrefix + this.redisKey,
110
- cursor,
111
- hScanOptions,
112
- ),
113
- );
114
- for (const { field, value } of remoteItems.tuples) {
115
- if (this.filterKeys(field)) {
116
- const parsedValue = JSON.parse(value);
117
- this.map.set(field, parsedValue);
118
- }
119
- }
120
- cursor = remoteItems.cursor;
121
- } while (cursor !== 0);
122
-
123
- // sharedTagsMap keys are cache keys (Redis strings). Tag-manifest maps
124
- // use tag names as hash fields — those never appear in SCAN.
125
- if (!this.customizedSync?.withoutOrphanCleanup) {
126
- await this.cleanupKeysNotInRedis();
127
- }
128
- } catch (error) {
129
- console.error('Error during initial sync:', error);
130
- throw error;
131
- }
132
- }
133
-
134
- private async cleanupKeysNotInRedis() {
135
- let cursor = 0;
136
- const scanOptions = { COUNT: this.querySize, MATCH: `${this.keyPrefix}*` };
137
- let remoteKeys: string[] = [];
138
- try {
139
- do {
140
- const remoteKeysPortion = await redisErrorHandler(
141
- 'SyncedMap.cleanupKeysNotInRedis(), operation: scan ' +
142
- this.keyPrefix,
143
- this.client.scan(cursor, scanOptions),
144
- );
145
- remoteKeys = remoteKeys.concat(remoteKeysPortion.keys);
146
- cursor = remoteKeysPortion.cursor;
147
- } while (cursor !== 0);
148
-
149
- const remoteKeysSet = new Set(
150
- remoteKeys.map((key) => key.substring(this.keyPrefix.length)),
151
- );
152
-
153
- const keysToDelete: string[] = [];
154
- for (const key of this.map.keys()) {
155
- const keyStr = key as unknown as string;
156
- if (!remoteKeysSet.has(keyStr) && this.filterKeys(keyStr)) {
157
- keysToDelete.push(keyStr);
158
- }
159
- }
160
-
161
- if (keysToDelete.length > 0) {
162
- await this.delete(keysToDelete);
163
- }
164
- } catch (error) {
165
- console.error('Error during cleanup of keys not in Redis:', error);
166
- throw error;
167
- }
168
- }
169
-
170
- private setupPeriodicResync() {
171
- if (this.resyncIntervalMs && this.resyncIntervalMs > 0) {
172
- setInterval(() => {
173
- this.initialSync().catch((error) => {
174
- console.error('Error during periodic resync:', error);
175
- });
176
- }, this.resyncIntervalMs);
177
- }
178
- }
179
-
180
- private async setupPubSub() {
181
- const syncHandler = async (message: string) => {
182
- const syncMessage: SyncMessage<V> = JSON.parse(message);
183
- if (syncMessage.type === 'insert') {
184
- if (syncMessage.key !== undefined && syncMessage.value !== undefined) {
185
- this.map.set(syncMessage.key, syncMessage.value);
186
- }
187
- } else if (syncMessage.type === 'delete') {
188
- if (syncMessage.keys) {
189
- for (const key of syncMessage.keys) {
190
- this.map.delete(key);
191
- }
192
- }
193
- }
194
- };
195
-
196
- const keyEventHandler = async (key: string, message: string) => {
197
- debug(
198
- 'yellow',
199
- 'SyncedMap.keyEventHandler() called with message',
200
- this.redisKey,
201
- message,
202
- key,
203
- );
204
- // const key = message;
205
- if (key.startsWith(this.keyPrefix)) {
206
- const keyInMap = key.substring(this.keyPrefix.length);
207
- if (this.filterKeys(keyInMap)) {
208
- debugVerbose(
209
- 'SyncedMap.keyEventHandler() key matches filter and will be deleted',
210
- this.redisKey,
211
- message,
212
- key,
213
- );
214
- await this.delete(keyInMap, true);
215
- }
216
- } else {
217
- debugVerbose(
218
- 'SyncedMap.keyEventHandler() key does not have prefix',
219
- this.redisKey,
220
- message,
221
- key,
222
- );
223
- }
224
- };
225
-
226
- try {
227
- // Attach error handler BEFORE subscribing so that errors during
228
- // subscribe or after connection loss are always caught and trigger
229
- // a reconnection. Previously this was attached after the subscribe()
230
- // calls, which meant if subscribe threw during an outage the handler
231
- // was never attached and the subscriber was permanently dead.
232
- this.subscriberClient.on('error', async (err) => {
233
- console.error('Subscriber client error:', err);
234
- await this.reconnectSubscriber();
235
- });
236
-
237
- const connectIfNeeded = async () => {
238
- // Avoid overlapping connect() calls which can lead to
239
- // "Socket already opened" errors when a connect is already in progress
240
- // or the socket is still open.
241
- if (!this.subscriberClient.isOpen && !this.subscriberClient.isReady) {
242
- await this.subscriberClient.connect();
243
- }
244
- };
245
-
246
- await connectIfNeeded().catch(async (error) => {
247
- console.error('Failed to connect subscriber client. Retrying...');
248
- try {
249
- await connectIfNeeded();
250
- } catch (err) {
251
- console.error('Failed to connect subscriber client.', err);
252
- throw err;
253
- }
254
- // keep original error in logs for context
255
- void error;
256
- });
257
-
258
- // Check if keyspace event configuration is set correctly
259
- if (
260
- (process.env.SKIP_KEYSPACE_CONFIG_CHECK || '').toUpperCase() !== 'TRUE'
261
- ) {
262
- const keyspaceEventConfig = (
263
- await this.subscriberClient.configGet('notify-keyspace-events')
264
- )?.['notify-keyspace-events'];
265
- if (!keyspaceEventConfig.includes('E')) {
266
- throw new Error(
267
- 'Keyspace event configuration is set to "' +
268
- keyspaceEventConfig +
269
- " but has to include 'E' for Keyevent events, " +
270
- 'published with __keyevent@<db>__ prefix. We recommend to set it to ' +
271
- "'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`",
272
- );
273
- }
274
- if (
275
- !keyspaceEventConfig.includes('A') &&
276
- !(
277
- keyspaceEventConfig.includes('x') &&
278
- keyspaceEventConfig.includes('e')
279
- )
280
- ) {
281
- throw new Error(
282
- 'Keyspace event configuration is set to "' +
283
- keyspaceEventConfig +
284
- " but has to include 'A' or 'x' and 'e' for expired and " +
285
- 'evicted events. We recommend to set it to ' +
286
- "'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`",
287
- );
288
- }
289
- }
290
-
291
- await Promise.all([
292
- // We use a custom channel for insert/delete For the following reason:
293
- // With custom channel we can delete multiple entries in one message. If we would listen to unlink / del we
294
- // could get thousands of messages for one revalidateTag (For example revalidateTag("algolia") would send an enormous amount of network packages)
295
- // Also we can send the value in the message for insert
296
- this.subscriberClient.subscribe(this.syncChannel, syncHandler),
297
- // Subscribe to Redis keyevent notifications for evicted and expired keys
298
- this.subscriberClient.subscribe(
299
- `__keyevent@${this.database}__:evicted`,
300
- keyEventHandler,
301
- ),
302
- this.subscriberClient.subscribe(
303
- `__keyevent@${this.database}__:expired`,
304
- keyEventHandler,
305
- ),
306
- ]);
307
- } catch (error) {
308
- console.error('Error setting up pub/sub client:', error);
309
- throw error;
310
- }
311
- }
312
-
313
- /**
314
- * Reconnect the subscriber client with exponential backoff.
315
- * Guarded by `isReconnecting` to prevent concurrent reconnection
316
- * attempts when multiple error events fire during an outage.
317
- * Retries indefinitely (with capped delay) so the subscriber
318
- * eventually recovers once Redis is available again.
319
- */
320
- private async reconnectSubscriber(): Promise<void> {
321
- if (this.isReconnecting) return;
322
- this.isReconnecting = true;
323
-
324
- try {
325
- let attempt = 0;
326
- while (true) {
327
- try {
328
- // Forcibly tear down the old subscriber so its internal reconnect
329
- // loop cannot come back to life once Redis recovers. `quit()` waits
330
- // for the server and can silently fail during an outage, leaving a
331
- // zombie client that still fires our error handler. Remove that
332
- // handler first so a delayed error from the old socket cannot tear
333
- // down the replacement subscriber we are about to create.
334
- try {
335
- this.subscriberClient.removeAllListeners('error');
336
- await this.subscriberClient.disconnect();
337
- } catch {
338
- // expected during outage
339
- }
340
- // Create a fresh subscriber client
341
- this.subscriberClient = this.client.duplicate();
342
- await this.setupPubSub();
343
- return; // success
344
- } catch (error) {
345
- const delay = Math.min(
346
- 500 * Math.pow(2, Math.min(attempt, 5)),
347
- 10_000,
348
- );
349
- attempt++;
350
- console.error(
351
- `Subscriber reconnect attempt ${attempt} failed, retrying in ${delay}ms`,
352
- error,
353
- );
354
- await new Promise((r) => setTimeout(r, delay));
355
- }
356
- }
357
- } finally {
358
- this.isReconnecting = false;
359
- }
360
- }
361
-
362
- public async waitUntilReady() {
363
- await this.setupLock;
364
- }
365
-
366
- public get(key: string): V | undefined {
367
- debugVerbose(
368
- 'SyncedMap.get() called with key',
369
- key,
370
- JSON.stringify(this.map.get(key))?.substring(0, 100),
371
- );
372
- return this.map.get(key);
373
- }
374
-
375
- public async set(key: string, value: V): Promise<void> {
376
- debugVerbose(
377
- 'SyncedMap.set() called with key',
378
- key,
379
- JSON.stringify(value)?.substring(0, 100),
380
- );
381
- this.map.set(key, value);
382
- const operations = [];
383
-
384
- // This is needed if we only want to sync delete commands. This is especially useful for non serializable data like a promise map
385
- if (this.customizedSync?.withoutSetSync) {
386
- return;
387
- }
388
- if (!this.customizedSync?.withoutRedisHashmap) {
389
- operations.push(
390
- redisErrorHandler(
391
- 'SyncedMap.set(), operation: hSet ' +
392
- this.syncChannel +
393
- ' ' +
394
- this.keyPrefix +
395
- ' ' +
396
- key,
397
- this.client.hSet(
398
- this.keyPrefix + this.redisKey,
399
- key as unknown as string,
400
- JSON.stringify(value),
401
- ),
402
- ),
403
- );
404
- }
405
-
406
- const insertMessage: SyncMessage<V> = {
407
- type: 'insert',
408
- key: key as unknown as string,
409
- value,
410
- };
411
- operations.push(
412
- redisErrorHandler(
413
- 'SyncedMap.set(), operation: publish ' +
414
- this.syncChannel +
415
- ' ' +
416
- this.keyPrefix +
417
- ' ' +
418
- key,
419
- this.client.publish(this.syncChannel, JSON.stringify(insertMessage)),
420
- ),
421
- );
422
- await Promise.all(operations);
423
- }
424
-
425
- public async delete(
426
- keys: string[] | string,
427
- withoutSyncMessage = false,
428
- ): Promise<void> {
429
- debugVerbose(
430
- 'SyncedMap.delete() called with keys',
431
- this.redisKey,
432
- keys,
433
- withoutSyncMessage,
434
- );
435
-
436
- const keysArray = Array.isArray(keys) ? keys : [keys];
437
- const operations = [];
438
-
439
- for (const key of keysArray) {
440
- this.map.delete(key);
441
- }
442
-
443
- if (!this.customizedSync?.withoutRedisHashmap) {
444
- operations.push(
445
- redisErrorHandler(
446
- 'SyncedMap.delete(), operation: hDel ' +
447
- this.syncChannel +
448
- ' ' +
449
- this.keyPrefix +
450
- ' ' +
451
- this.redisKey +
452
- ' ' +
453
- keysArray,
454
- this.client.hDel(this.keyPrefix + this.redisKey, keysArray),
455
- ),
456
- );
457
- }
458
-
459
- if (!withoutSyncMessage) {
460
- const deletionMessage: SyncMessage<V> = {
461
- type: 'delete',
462
- keys: keysArray,
463
- };
464
- operations.push(
465
- redisErrorHandler(
466
- 'SyncedMap.delete(), operation: publish ' +
467
- this.syncChannel +
468
- ' ' +
469
- this.keyPrefix +
470
- ' ' +
471
- keysArray,
472
- this.client.publish(
473
- this.syncChannel,
474
- JSON.stringify(deletionMessage),
475
- ),
476
- ),
477
- );
478
- }
479
-
480
- await Promise.all(operations);
481
- debugVerbose(
482
- 'SyncedMap.delete() finished operations',
483
- this.redisKey,
484
- keys,
485
- operations.length,
486
- );
487
- }
488
-
489
- public has(key: string): boolean {
490
- return this.map.has(key);
491
- }
492
-
493
- public entries(): IterableIterator<[string, V]> {
494
- return this.map.entries();
495
- }
496
- }
package/src/ZodHandler.ts DELETED
@@ -1,45 +0,0 @@
1
- /**
2
- * This handler is used to validate the arguments of the get, set, revalidateTag methods against a zod schema.
3
- * This is mainly used to test new nextjs versions and to ensure that the arguments are still valid. As we
4
- * saw in the past, that arguments against the cache handler changed between different nextjs versions very often (even between patch versions).
5
- */
6
-
7
- import RedisStringsHandler, {
8
- CreateRedisStringsHandlerOptions,
9
- } from './RedisStringsHandler';
10
- import { debugVerbose } from './utils/debug';
11
-
12
- let cachedHandler: RedisStringsHandler;
13
-
14
- export default class CachedHandler {
15
- constructor(options: CreateRedisStringsHandlerOptions) {
16
- if (!cachedHandler) {
17
- console.log('created development cached handler');
18
- cachedHandler = new RedisStringsHandler(options);
19
- }
20
- }
21
- get(
22
- ...args: Parameters<RedisStringsHandler['get']>
23
- ): ReturnType<RedisStringsHandler['get']> {
24
- debugVerbose('CachedHandler.get called with', args);
25
- return cachedHandler.get(...args);
26
- }
27
- set(
28
- ...args: Parameters<RedisStringsHandler['set']>
29
- ): ReturnType<RedisStringsHandler['set']> {
30
- debugVerbose('CachedHandler.set called with', args);
31
- return cachedHandler.set(...args);
32
- }
33
- revalidateTag(
34
- ...args: Parameters<RedisStringsHandler['revalidateTag']>
35
- ): ReturnType<RedisStringsHandler['revalidateTag']> {
36
- debugVerbose('CachedHandler.revalidateTag called with', args);
37
- return cachedHandler.revalidateTag(...args);
38
- }
39
- resetRequestCache(
40
- ...args: Parameters<RedisStringsHandler['resetRequestCache']>
41
- ): ReturnType<RedisStringsHandler['resetRequestCache']> {
42
- // debug("CachedHandler.resetRequestCache called with", args);
43
- return cachedHandler.resetRequestCache(...args);
44
- }
45
- }
package/src/index.ts DELETED
@@ -1,23 +0,0 @@
1
- import CachedHandler from './CachedHandler';
2
- export default CachedHandler;
3
-
4
- import RedisStringsHandler from './RedisStringsHandler';
5
- export { RedisStringsHandler };
6
- export type { CreateRedisStringsHandlerOptions } from './RedisStringsHandler';
7
-
8
- export { jsonCacheValueSerializer } from './serializer';
9
- export type { CacheValueSerializer } from './serializer';
10
- export { bufferAndMapReplacer, bufferAndMapReviver } from './utils/json';
11
- export { resolveCacheEntryTtlSeconds } from './utils/cacheTtl';
12
- export type {
13
- ResolveCacheEntryTtlContext,
14
- ResolveCacheEntryTtlData,
15
- ResolveCacheEntryTtlOptions,
16
- } from './utils/cacheTtl';
17
- export { shouldDeferRedisConnection } from './utils/redisConnection';
18
-
19
- import {
20
- redisCacheHandler,
21
- getRedisCacheComponentsHandler,
22
- } from './CacheComponentsHandler';
23
- export { redisCacheHandler, getRedisCacheComponentsHandler };
package/src/serializer.ts DELETED
@@ -1,59 +0,0 @@
1
- /**
2
- * Pluggable wire-format codec for Redis string values used by `RedisStringsHandler`.
3
- *
4
- * The serializer is the single point at which an in-memory `CacheEntry` becomes the
5
- * string written to Redis (and back). Plugging in a custom serializer lets you add
6
- * compression (gzip/brotli), encryption, or any other custom encoding without forking
7
- * this package or losing the existing dedup / batch / keyspace features.
8
- *
9
- * Both `serialize` and `deserialize` may return either a value directly or a `Promise`,
10
- * which enables non-blocking async codecs such as stream-based compression
11
- * (`zlib.brotliCompress`) or encryption (`crypto.subtle`). Synchronous implementations
12
- * continue to work unchanged - awaiting a plain value is a no-op.
13
- *
14
- * The default export {@link jsonCacheValueSerializer} is `JSON.stringify` /
15
- * `JSON.parse` paired with {@link bufferAndMapReplacer} / {@link bufferAndMapReviver},
16
- * so native `Buffer` and `Map` values inside a `CacheEntry` round-trip transparently
17
- * (this is required for Next.js RSC payloads).
18
- *
19
- * Operational note: changing the serializer (or any of its parameters such as a
20
- * compression level or encryption key) makes existing Redis keys unreadable, because
21
- * the deserializer will fail or return `null` for entries written by the previous
22
- * format. Either flush the affected keys, bump `keyPrefix`, or migrate values
23
- * out-of-band before deploying a new serializer.
24
- */
25
- import type { CacheEntry } from './RedisStringsHandler';
26
- import { bufferAndMapReplacer, bufferAndMapReviver } from './utils/json';
27
-
28
- export type CacheValueSerializer = {
29
- /**
30
- * Encode an in-memory `CacheEntry` into the string written to Redis.
31
- * May return a `Promise` for async codecs (e.g. compression, encryption).
32
- */
33
- serialize(value: CacheEntry): string | Promise<string>;
34
- /**
35
- * Decode a string read from Redis back into a `CacheEntry`.
36
- * Returning `null` (or a `Promise<null>`) signals "treat as cache miss" -
37
- * the handler will return `null` from `get()` without surfacing an error.
38
- * May return a `Promise` for async codecs.
39
- */
40
- deserialize(stored: string): CacheEntry | null | Promise<CacheEntry | null>;
41
- };
42
-
43
- /**
44
- * Default serializer used by `RedisStringsHandler` when no `valueSerializer` is
45
- * configured. Wraps `JSON.stringify` / `JSON.parse` with this package's
46
- * {@link bufferAndMapReplacer} / {@link bufferAndMapReviver} so native `Buffer`
47
- * and `Map` values inside a `CacheEntry` survive the round-trip.
48
- *
49
- * Exported as a singleton so consumers can compare against the default by
50
- * reference (e.g. to detect that no custom serializer was configured).
51
- */
52
- export const jsonCacheValueSerializer: CacheValueSerializer = {
53
- serialize(value) {
54
- return JSON.stringify(value, bufferAndMapReplacer);
55
- },
56
- deserialize(stored) {
57
- return JSON.parse(stored, bufferAndMapReviver) as CacheEntry | null;
58
- },
59
- };
@@ -1,71 +0,0 @@
1
- export type ResolveCacheEntryTtlContext = {
2
- revalidate?: number | false;
3
- cacheControl?: { revalidate?: number | false; expire?: number | undefined };
4
- };
5
-
6
- export type ResolveCacheEntryTtlData = {
7
- kind?: string;
8
- revalidate?: number | false;
9
- } | null;
10
-
11
- export type ResolveCacheEntryTtlOptions = {
12
- estimateExpireAge: (staleAge: number) => number;
13
- defaultStaleAge: number;
14
- };
15
-
16
- /**
17
- * Resolves the Redis TTL (seconds) for an ISR / incremental-cache entry.
18
- *
19
- * Official Next.js semantics (cache-handler-redis example + self-hosting docs):
20
- * - Key TTL on `cacheControl.expire`, never on `revalidate` alone.
21
- * - Past `revalidate` an entry is only stale (SWR); evicting at that boundary
22
- * would defeat background refresh.
23
- * - When no finite `expire` is provided, fall back to `estimateExpireAge(revalidate)`
24
- * for legacy Pages Router / pre-cacheLife callers that only pass `revalidate`.
25
- * - When neither `expire` nor `revalidate` is available, return `undefined` (no TTL;
26
- * rely on tag-based invalidation). This is intentional — not a missing
27
- * `defaultStaleAge` fallback. `revalidate: false` is a separate branch above
28
- * and is covered by unit + Pages Router integration tests (`/static-forever`).
29
- */
30
- export function resolveCacheEntryTtlSeconds(
31
- ctx: ResolveCacheEntryTtlContext,
32
- data: ResolveCacheEntryTtlData,
33
- options: ResolveCacheEntryTtlOptions,
34
- ): number | undefined {
35
- const expire = ctx.cacheControl?.expire;
36
-
37
- if (Number.isFinite(expire) && expire! > 0) {
38
- return Math.max(1, Math.ceil(expire!));
39
- }
40
-
41
- const revalidate = resolveRevalidateValue(ctx, data);
42
-
43
- if (revalidate === false) {
44
- return options.estimateExpireAge(options.defaultStaleAge);
45
- }
46
-
47
- if (revalidate && Number.isSafeInteger(revalidate) && revalidate > 0) {
48
- return options.estimateExpireAge(revalidate);
49
- }
50
-
51
- return undefined;
52
- }
53
-
54
- function resolveRevalidateValue(
55
- ctx: ResolveCacheEntryTtlContext,
56
- data: ResolveCacheEntryTtlData,
57
- ): number | false | undefined {
58
- if (data?.kind === 'FETCH' && data.revalidate !== undefined) {
59
- return data.revalidate;
60
- }
61
- if (ctx.revalidate !== undefined) {
62
- return ctx.revalidate;
63
- }
64
- if (ctx.cacheControl?.revalidate !== undefined) {
65
- return ctx.cacheControl.revalidate;
66
- }
67
- if (data?.revalidate !== undefined) {
68
- return data.revalidate;
69
- }
70
- return undefined;
71
- }