@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/src/SyncedMap.ts DELETED
@@ -1,482 +0,0 @@
1
- // SyncedMap.ts
2
- import { Client, redisErrorHandler } from './RedisStringsHandler';
3
- import { debugVerbose, debug } from './utils/debug';
4
-
5
- type CustomizedSync = {
6
- withoutRedisHashmap?: boolean;
7
- withoutSetSync?: boolean;
8
- };
9
-
10
- type SyncedMapOptions = {
11
- client: Client;
12
- keyPrefix: string;
13
- redisKey: string; // Redis Hash key
14
- database: number;
15
- querySize: number;
16
- filterKeys: (key: string) => boolean;
17
- resyncIntervalMs?: number;
18
- customizedSync?: CustomizedSync;
19
- };
20
-
21
- export type SyncMessage<V> = {
22
- type: 'insert' | 'delete';
23
- key?: string;
24
- value?: V;
25
- keys?: string[];
26
- };
27
-
28
- const SYNC_CHANNEL_SUFFIX = ':sync-channel:';
29
- export class SyncedMap<V> {
30
- private client: Client;
31
- private subscriberClient: Client;
32
- private map: Map<string, V>;
33
- private keyPrefix: string;
34
- private syncChannel: string;
35
- private redisKey: string;
36
- private database: number;
37
- private querySize: number;
38
- private filterKeys: (key: string) => boolean;
39
- private resyncIntervalMs?: number;
40
- private customizedSync?: CustomizedSync;
41
-
42
- private setupLock: Promise<void>;
43
- private setupLockResolve!: () => void;
44
- private isReconnecting = false;
45
-
46
- constructor(options: SyncedMapOptions) {
47
- this.client = options.client;
48
- this.keyPrefix = options.keyPrefix;
49
- this.redisKey = options.redisKey;
50
- this.syncChannel = `${options.keyPrefix}${SYNC_CHANNEL_SUFFIX}${options.redisKey}`;
51
- this.database = options.database;
52
- this.querySize = options.querySize;
53
- this.filterKeys = options.filterKeys;
54
- this.resyncIntervalMs = options.resyncIntervalMs;
55
- this.customizedSync = options.customizedSync;
56
-
57
- this.map = new Map<string, V>();
58
- this.subscriberClient = this.client.duplicate();
59
- this.setupLock = new Promise<void>((resolve) => {
60
- this.setupLockResolve = resolve;
61
- });
62
-
63
- this.setup().catch((error) => {
64
- console.error('Failed to setup SyncedMap:', error);
65
- throw error;
66
- });
67
- }
68
-
69
- private async setup() {
70
- let setupPromises: Promise<void>[] = [];
71
- if (!this.customizedSync?.withoutRedisHashmap) {
72
- setupPromises.push(this.initialSync());
73
- this.setupPeriodicResync();
74
- }
75
- setupPromises.push(this.setupPubSub());
76
- await Promise.all(setupPromises);
77
- this.setupLockResolve();
78
- }
79
-
80
- private async initialSync() {
81
- let cursor = 0;
82
- const hScanOptions = { COUNT: this.querySize };
83
-
84
- try {
85
- do {
86
- const remoteItems = await redisErrorHandler(
87
- 'SyncedMap.initialSync(), operation: hScan ' +
88
- this.syncChannel +
89
- ' ' +
90
- this.keyPrefix +
91
- ' ' +
92
- this.redisKey +
93
- ' ' +
94
- cursor +
95
- ' ' +
96
- this.querySize,
97
- this.client.hScan(
98
- this.keyPrefix + this.redisKey,
99
- cursor,
100
- hScanOptions,
101
- ),
102
- );
103
- for (const { field, value } of remoteItems.tuples) {
104
- if (this.filterKeys(field)) {
105
- const parsedValue = JSON.parse(value);
106
- this.map.set(field, parsedValue);
107
- }
108
- }
109
- cursor = remoteItems.cursor;
110
- } while (cursor !== 0);
111
-
112
- // Clean up keys not in Redis
113
- await this.cleanupKeysNotInRedis();
114
- } catch (error) {
115
- console.error('Error during initial sync:', error);
116
- throw error;
117
- }
118
- }
119
-
120
- private async cleanupKeysNotInRedis() {
121
- let cursor = 0;
122
- const scanOptions = { COUNT: this.querySize, MATCH: `${this.keyPrefix}*` };
123
- let remoteKeys: string[] = [];
124
- try {
125
- do {
126
- const remoteKeysPortion = await redisErrorHandler(
127
- 'SyncedMap.cleanupKeysNotInRedis(), operation: scan ' +
128
- this.keyPrefix,
129
- this.client.scan(cursor, scanOptions),
130
- );
131
- remoteKeys = remoteKeys.concat(remoteKeysPortion.keys);
132
- cursor = remoteKeysPortion.cursor;
133
- } while (cursor !== 0);
134
-
135
- const remoteKeysSet = new Set(
136
- remoteKeys.map((key) => key.substring(this.keyPrefix.length)),
137
- );
138
-
139
- const keysToDelete: string[] = [];
140
- for (const key of this.map.keys()) {
141
- const keyStr = key as unknown as string;
142
- if (!remoteKeysSet.has(keyStr) && this.filterKeys(keyStr)) {
143
- keysToDelete.push(keyStr);
144
- }
145
- }
146
-
147
- if (keysToDelete.length > 0) {
148
- await this.delete(keysToDelete);
149
- }
150
- } catch (error) {
151
- console.error('Error during cleanup of keys not in Redis:', error);
152
- throw error;
153
- }
154
- }
155
-
156
- private setupPeriodicResync() {
157
- if (this.resyncIntervalMs && this.resyncIntervalMs > 0) {
158
- setInterval(() => {
159
- this.initialSync().catch((error) => {
160
- console.error('Error during periodic resync:', error);
161
- });
162
- }, this.resyncIntervalMs);
163
- }
164
- }
165
-
166
- private async setupPubSub() {
167
- const syncHandler = async (message: string) => {
168
- const syncMessage: SyncMessage<V> = JSON.parse(message);
169
- if (syncMessage.type === 'insert') {
170
- if (syncMessage.key !== undefined && syncMessage.value !== undefined) {
171
- this.map.set(syncMessage.key, syncMessage.value);
172
- }
173
- } else if (syncMessage.type === 'delete') {
174
- if (syncMessage.keys) {
175
- for (const key of syncMessage.keys) {
176
- this.map.delete(key);
177
- }
178
- }
179
- }
180
- };
181
-
182
- const keyEventHandler = async (key: string, message: string) => {
183
- debug(
184
- 'yellow',
185
- 'SyncedMap.keyEventHandler() called with message',
186
- this.redisKey,
187
- message,
188
- key,
189
- );
190
- // const key = message;
191
- if (key.startsWith(this.keyPrefix)) {
192
- const keyInMap = key.substring(this.keyPrefix.length);
193
- if (this.filterKeys(keyInMap)) {
194
- debugVerbose(
195
- 'SyncedMap.keyEventHandler() key matches filter and will be deleted',
196
- this.redisKey,
197
- message,
198
- key,
199
- );
200
- await this.delete(keyInMap, true);
201
- }
202
- } else {
203
- debugVerbose(
204
- 'SyncedMap.keyEventHandler() key does not have prefix',
205
- this.redisKey,
206
- message,
207
- key,
208
- );
209
- }
210
- };
211
-
212
- try {
213
- // Attach error handler BEFORE subscribing so that errors during
214
- // subscribe or after connection loss are always caught and trigger
215
- // a reconnection. Previously this was attached after the subscribe()
216
- // calls, which meant if subscribe threw during an outage the handler
217
- // was never attached and the subscriber was permanently dead.
218
- this.subscriberClient.on('error', async (err) => {
219
- console.error('Subscriber client error:', err);
220
- await this.reconnectSubscriber();
221
- });
222
-
223
- const connectIfNeeded = async () => {
224
- // Avoid overlapping connect() calls which can lead to
225
- // "Socket already opened" errors when a connect is already in progress
226
- // or the socket is still open.
227
- if (!this.subscriberClient.isOpen && !this.subscriberClient.isReady) {
228
- await this.subscriberClient.connect();
229
- }
230
- };
231
-
232
- await connectIfNeeded().catch(async (error) => {
233
- console.error('Failed to connect subscriber client. Retrying...');
234
- try {
235
- await connectIfNeeded();
236
- } catch (err) {
237
- console.error('Failed to connect subscriber client.', err);
238
- throw err;
239
- }
240
- // keep original error in logs for context
241
- void error;
242
- });
243
-
244
- // Check if keyspace event configuration is set correctly
245
- if (
246
- (process.env.SKIP_KEYSPACE_CONFIG_CHECK || '').toUpperCase() !== 'TRUE'
247
- ) {
248
- const keyspaceEventConfig = (
249
- await this.subscriberClient.configGet('notify-keyspace-events')
250
- )?.['notify-keyspace-events'];
251
- if (!keyspaceEventConfig.includes('E')) {
252
- throw new Error(
253
- 'Keyspace event configuration is set to "' +
254
- keyspaceEventConfig +
255
- " but has to include 'E' for Keyevent events, " +
256
- 'published with __keyevent@<db>__ prefix. We recommend to set it to ' +
257
- "'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`",
258
- );
259
- }
260
- if (
261
- !keyspaceEventConfig.includes('A') &&
262
- !(
263
- keyspaceEventConfig.includes('x') &&
264
- keyspaceEventConfig.includes('e')
265
- )
266
- ) {
267
- throw new Error(
268
- 'Keyspace event configuration is set to "' +
269
- keyspaceEventConfig +
270
- " but has to include 'A' or 'x' and 'e' for expired and " +
271
- 'evicted events. We recommend to set it to ' +
272
- "'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`",
273
- );
274
- }
275
- }
276
-
277
- await Promise.all([
278
- // We use a custom channel for insert/delete For the following reason:
279
- // With custom channel we can delete multiple entries in one message. If we would listen to unlink / del we
280
- // could get thousands of messages for one revalidateTag (For example revalidateTag("algolia") would send an enormous amount of network packages)
281
- // Also we can send the value in the message for insert
282
- this.subscriberClient.subscribe(this.syncChannel, syncHandler),
283
- // Subscribe to Redis keyevent notifications for evicted and expired keys
284
- this.subscriberClient.subscribe(
285
- `__keyevent@${this.database}__:evicted`,
286
- keyEventHandler,
287
- ),
288
- this.subscriberClient.subscribe(
289
- `__keyevent@${this.database}__:expired`,
290
- keyEventHandler,
291
- ),
292
- ]);
293
- } catch (error) {
294
- console.error('Error setting up pub/sub client:', error);
295
- throw error;
296
- }
297
- }
298
-
299
- /**
300
- * Reconnect the subscriber client with exponential backoff.
301
- * Guarded by `isReconnecting` to prevent concurrent reconnection
302
- * attempts when multiple error events fire during an outage.
303
- * Retries indefinitely (with capped delay) so the subscriber
304
- * eventually recovers once Redis is available again.
305
- */
306
- private async reconnectSubscriber(): Promise<void> {
307
- if (this.isReconnecting) return;
308
- this.isReconnecting = true;
309
-
310
- try {
311
- let attempt = 0;
312
- while (true) {
313
- try {
314
- // Forcibly tear down the old subscriber so its internal reconnect
315
- // loop cannot come back to life once Redis recovers. `quit()` waits
316
- // for the server and can silently fail during an outage, leaving a
317
- // zombie client that still fires our error handler. Remove that
318
- // handler first so a delayed error from the old socket cannot tear
319
- // down the replacement subscriber we are about to create.
320
- try {
321
- this.subscriberClient.removeAllListeners('error');
322
- await this.subscriberClient.disconnect();
323
- } catch {
324
- // expected during outage
325
- }
326
- // Create a fresh subscriber client
327
- this.subscriberClient = this.client.duplicate();
328
- await this.setupPubSub();
329
- return; // success
330
- } catch (error) {
331
- const delay = Math.min(
332
- 500 * Math.pow(2, Math.min(attempt, 5)),
333
- 10_000,
334
- );
335
- attempt++;
336
- console.error(
337
- `Subscriber reconnect attempt ${attempt} failed, retrying in ${delay}ms`,
338
- error,
339
- );
340
- await new Promise((r) => setTimeout(r, delay));
341
- }
342
- }
343
- } finally {
344
- this.isReconnecting = false;
345
- }
346
- }
347
-
348
- public async waitUntilReady() {
349
- await this.setupLock;
350
- }
351
-
352
- public get(key: string): V | undefined {
353
- debugVerbose(
354
- 'SyncedMap.get() called with key',
355
- key,
356
- JSON.stringify(this.map.get(key))?.substring(0, 100),
357
- );
358
- return this.map.get(key);
359
- }
360
-
361
- public async set(key: string, value: V): Promise<void> {
362
- debugVerbose(
363
- 'SyncedMap.set() called with key',
364
- key,
365
- JSON.stringify(value)?.substring(0, 100),
366
- );
367
- this.map.set(key, value);
368
- const operations = [];
369
-
370
- // This is needed if we only want to sync delete commands. This is especially useful for non serializable data like a promise map
371
- if (this.customizedSync?.withoutSetSync) {
372
- return;
373
- }
374
- if (!this.customizedSync?.withoutRedisHashmap) {
375
- operations.push(
376
- redisErrorHandler(
377
- 'SyncedMap.set(), operation: hSet ' +
378
- this.syncChannel +
379
- ' ' +
380
- this.keyPrefix +
381
- ' ' +
382
- key,
383
- this.client.hSet(
384
- this.keyPrefix + this.redisKey,
385
- key as unknown as string,
386
- JSON.stringify(value),
387
- ),
388
- ),
389
- );
390
- }
391
-
392
- const insertMessage: SyncMessage<V> = {
393
- type: 'insert',
394
- key: key as unknown as string,
395
- value,
396
- };
397
- operations.push(
398
- redisErrorHandler(
399
- 'SyncedMap.set(), operation: publish ' +
400
- this.syncChannel +
401
- ' ' +
402
- this.keyPrefix +
403
- ' ' +
404
- key,
405
- this.client.publish(this.syncChannel, JSON.stringify(insertMessage)),
406
- ),
407
- );
408
- await Promise.all(operations);
409
- }
410
-
411
- public async delete(
412
- keys: string[] | string,
413
- withoutSyncMessage = false,
414
- ): Promise<void> {
415
- debugVerbose(
416
- 'SyncedMap.delete() called with keys',
417
- this.redisKey,
418
- keys,
419
- withoutSyncMessage,
420
- );
421
-
422
- const keysArray = Array.isArray(keys) ? keys : [keys];
423
- const operations = [];
424
-
425
- for (const key of keysArray) {
426
- this.map.delete(key);
427
- }
428
-
429
- if (!this.customizedSync?.withoutRedisHashmap) {
430
- operations.push(
431
- redisErrorHandler(
432
- 'SyncedMap.delete(), operation: hDel ' +
433
- this.syncChannel +
434
- ' ' +
435
- this.keyPrefix +
436
- ' ' +
437
- this.redisKey +
438
- ' ' +
439
- keysArray,
440
- this.client.hDel(this.keyPrefix + this.redisKey, keysArray),
441
- ),
442
- );
443
- }
444
-
445
- if (!withoutSyncMessage) {
446
- const deletionMessage: SyncMessage<V> = {
447
- type: 'delete',
448
- keys: keysArray,
449
- };
450
- operations.push(
451
- redisErrorHandler(
452
- 'SyncedMap.delete(), operation: publish ' +
453
- this.syncChannel +
454
- ' ' +
455
- this.keyPrefix +
456
- ' ' +
457
- keysArray,
458
- this.client.publish(
459
- this.syncChannel,
460
- JSON.stringify(deletionMessage),
461
- ),
462
- ),
463
- );
464
- }
465
-
466
- await Promise.all(operations);
467
- debugVerbose(
468
- 'SyncedMap.delete() finished operations',
469
- this.redisKey,
470
- keys,
471
- operations.length,
472
- );
473
- }
474
-
475
- public has(key: string): boolean {
476
- return this.map.has(key);
477
- }
478
-
479
- public entries(): IterableIterator<[string, V]> {
480
- return this.map.entries();
481
- }
482
- }
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,16 +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
-
12
- import {
13
- redisCacheHandler,
14
- getRedisCacheComponentsHandler,
15
- } from './CacheComponentsHandler';
16
- 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,30 +0,0 @@
1
- export function debug(
2
- color:
3
- | 'red'
4
- | 'blue'
5
- | 'green'
6
- | 'yellow'
7
- | 'cyan'
8
- | 'white'
9
- | 'none' = 'none',
10
- ...args: unknown[]
11
- ): void {
12
- const colorCode = {
13
- red: '\x1b[31m',
14
- blue: '\x1b[34m',
15
- green: '\x1b[32m',
16
- yellow: '\x1b[33m',
17
- cyan: '\x1b[36m',
18
- white: '\x1b[37m',
19
- none: '',
20
- };
21
- if (process.env.DEBUG_CACHE_HANDLER) {
22
- console.log(colorCode[color], 'DEBUG CACHE HANDLER: ', ...args);
23
- }
24
- }
25
-
26
- export function debugVerbose(color: string, ...args: unknown[]) {
27
- if (process.env.DEBUG_CACHE_HANDLER_VERBOSE_VERBOSE) {
28
- console.log('\x1b[35m', 'DEBUG SYNCED MAP: ', ...args);
29
- }
30
- }
package/src/utils/json.ts DELETED
@@ -1,49 +0,0 @@
1
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
- export function bufferAndMapReviver(_: string, value: any): any {
3
- if (value && typeof value === 'object' && typeof value.$binary === 'string') {
4
- return Buffer.from(value.$binary, 'base64');
5
- }
6
- if (
7
- value &&
8
- typeof value === 'object' &&
9
- typeof value.$map === 'object' &&
10
- !!value.$map
11
- ) {
12
- return new Map(
13
- Object.entries(value.$map).map(([key, value]) => {
14
- const revivedValue = bufferAndMapReviver('', value);
15
- return [key, revivedValue];
16
- }),
17
- );
18
- }
19
- return value;
20
- }
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
- export function bufferAndMapReplacer(_: string, value: any): any {
23
- if (Buffer.isBuffer(value)) {
24
- return {
25
- $binary: value.toString('base64'),
26
- };
27
- }
28
- if (
29
- value &&
30
- typeof value === 'object' &&
31
- value?.type === 'Buffer' &&
32
- Array.isArray(value.data)
33
- ) {
34
- return {
35
- $binary: Buffer.from(value.data).toString('base64'),
36
- };
37
- }
38
- if (value && typeof value === 'object' && value instanceof Map) {
39
- return {
40
- $map: Object.fromEntries(
41
- Array.from(value.entries()).map(([key, value]) => {
42
- const replacedValue = bufferAndMapReplacer('', value);
43
- return [key, replacedValue];
44
- }),
45
- ),
46
- };
47
- }
48
- return value;
49
- }