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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/dist/index.js +27 -20
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +27 -20
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +6 -1
  6. package/.cursor/skills/testing/SKILL.md +0 -121
  7. package/.github/workflows/ci.yml +0 -309
  8. package/.github/workflows/pages.yml +0 -38
  9. package/.github/workflows/release.yml +0 -58
  10. package/.prettierrc.json +0 -6
  11. package/.vscode/settings.json +0 -10
  12. package/ARCHITECTURE.md +0 -470
  13. package/commitlint.config.cjs +0 -4
  14. package/docker/redis.conf +0 -1
  15. package/docs/index.html +0 -140
  16. package/docs/robots.txt +0 -4
  17. package/docs/sitemap.xml +0 -8
  18. package/docs/styles.css +0 -141
  19. package/eslint.config.mjs +0 -26
  20. package/hooks/commit-msg +0 -10
  21. package/hooks/pre-commit +0 -11
  22. package/playwright.config.ts +0 -18
  23. package/release.config.cjs +0 -12
  24. package/scripts/prepare.sh +0 -7
  25. package/scripts/setup-git.sh +0 -8
  26. package/scripts/setup-repo-name.sh +0 -17
  27. package/scripts/vitest-run-staged.cjs +0 -44
  28. package/src/CacheComponentsHandler.ts +0 -597
  29. package/src/CachedHandler.ts +0 -53
  30. package/src/DeduplicatedRequestHandler.ts +0 -110
  31. package/src/RedisStringsHandler.ts +0 -949
  32. package/src/SyncedMap.ts +0 -496
  33. package/src/ZodHandler.ts +0 -45
  34. package/src/index.ts +0 -23
  35. package/src/serializer.ts +0 -59
  36. package/src/utils/cacheTtl.ts +0 -71
  37. package/src/utils/compareAndUnlink.ts +0 -29
  38. package/src/utils/debug.ts +0 -30
  39. package/src/utils/json.ts +0 -49
  40. package/src/utils/prefix.ts +0 -44
  41. package/src/utils/redisConnection.ts +0 -17
  42. package/src/utils/tagRevalidation.ts +0 -162
  43. package/test/README.md +0 -194
  44. package/test/nextjs-test-projects/next-app-15-4-11/README.md +0 -36
  45. package/test/nextjs-test-projects/next-app-15-4-11/eslint.config.mjs +0 -16
  46. package/test/nextjs-test-projects/next-app-15-4-11/next.config.js +0 -9
  47. package/test/nextjs-test-projects/next-app-15-4-11/package-lock.json +0 -5969
  48. package/test/nextjs-test-projects/next-app-15-4-11/package.json +0 -29
  49. package/test/nextjs-test-projects/next-app-15-4-11/pnpm-lock.yaml +0 -3726
  50. package/test/nextjs-test-projects/next-app-15-4-11/postcss.config.mjs +0 -5
  51. package/test/nextjs-test-projects/next-app-15-4-11/public/file.svg +0 -1
  52. package/test/nextjs-test-projects/next-app-15-4-11/public/globe.svg +0 -1
  53. package/test/nextjs-test-projects/next-app-15-4-11/public/next.svg +0 -1
  54. package/test/nextjs-test-projects/next-app-15-4-11/public/vercel.svg +0 -1
  55. package/test/nextjs-test-projects/next-app-15-4-11/public/window.svg +0 -1
  56. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/cached-static-fetch/route.ts +0 -18
  57. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  58. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidatePath/route.ts +0 -15
  59. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidateTag/route.ts +0 -15
  60. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidated-fetch/route.ts +0 -17
  61. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/uncached-fetch/route.ts +0 -15
  62. package/test/nextjs-test-projects/next-app-15-4-11/src/app/globals.css +0 -26
  63. package/test/nextjs-test-projects/next-app-15-4-11/src/app/layout.tsx +0 -59
  64. package/test/nextjs-test-projects/next-app-15-4-11/src/app/page.tsx +0 -755
  65. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  66. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  67. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  68. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  69. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  70. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  71. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  72. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  73. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  74. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  75. package/test/nextjs-test-projects/next-app-15-4-11/src/app/revalidation-interface.tsx +0 -267
  76. package/test/nextjs-test-projects/next-app-15-4-11/tsconfig.json +0 -27
  77. package/test/nextjs-test-projects/next-app-16-0-11/README.md +0 -36
  78. package/test/nextjs-test-projects/next-app-16-0-11/eslint.config.mjs +0 -18
  79. package/test/nextjs-test-projects/next-app-16-0-11/next.config.ts +0 -10
  80. package/test/nextjs-test-projects/next-app-16-0-11/package.json +0 -28
  81. package/test/nextjs-test-projects/next-app-16-0-11/pnpm-lock.yaml +0 -4163
  82. package/test/nextjs-test-projects/next-app-16-0-11/postcss.config.mjs +0 -7
  83. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/cached-static-fetch/route.ts +0 -18
  84. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  85. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidatePath/route.ts +0 -15
  86. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidateTag/route.ts +0 -20
  87. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidated-fetch/route.ts +0 -17
  88. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/uncached-fetch/route.ts +0 -15
  89. package/test/nextjs-test-projects/next-app-16-0-11/src/app/favicon.ico +0 -0
  90. package/test/nextjs-test-projects/next-app-16-0-11/src/app/globals.css +0 -26
  91. package/test/nextjs-test-projects/next-app-16-0-11/src/app/layout.tsx +0 -59
  92. package/test/nextjs-test-projects/next-app-16-0-11/src/app/page.tsx +0 -755
  93. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  94. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  95. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  96. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  97. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  98. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  99. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  100. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  101. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  102. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  103. package/test/nextjs-test-projects/next-app-16-0-11/src/app/revalidation-interface.tsx +0 -267
  104. package/test/nextjs-test-projects/next-app-16-0-11/src/app/update-tag-test/page.tsx +0 -25
  105. package/test/nextjs-test-projects/next-app-16-0-11/tsconfig.json +0 -34
  106. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/README.md +0 -36
  107. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/cache-handler.js +0 -3
  108. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/eslint.config.mjs +0 -18
  109. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/next.config.ts +0 -15
  110. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/package.json +0 -28
  111. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/pnpm-lock.yaml +0 -4206
  112. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/postcss.config.mjs +0 -7
  113. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/file.svg +0 -1
  114. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/globe.svg +0 -1
  115. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/next.svg +0 -1
  116. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/vercel.svg +0 -1
  117. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/window.svg +0 -1
  118. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  119. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  120. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  121. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  122. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  123. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  124. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  125. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  126. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/page.tsx +0 -112
  127. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  128. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  129. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  130. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  131. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  132. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  133. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/favicon.ico +0 -0
  134. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/globals.css +0 -26
  135. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/layout.tsx +0 -57
  136. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/page.tsx +0 -755
  137. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/revalidation-interface.tsx +0 -267
  138. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/update-tag-test/page.tsx +0 -22
  139. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/tsconfig.json +0 -34
  140. package/test/nextjs-test-projects/next-app-16-2-6/README.md +0 -36
  141. package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +0 -18
  142. package/test/nextjs-test-projects/next-app-16-2-6/next.config.ts +0 -7
  143. package/test/nextjs-test-projects/next-app-16-2-6/package.json +0 -28
  144. package/test/nextjs-test-projects/next-app-16-2-6/pnpm-lock.yaml +0 -4165
  145. package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +0 -7
  146. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/cached-static-fetch/route.ts +0 -18
  147. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  148. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidatePath/route.ts +0 -15
  149. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidateTag/route.ts +0 -20
  150. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidated-fetch/route.ts +0 -17
  151. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/uncached-fetch/route.ts +0 -15
  152. package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
  153. package/test/nextjs-test-projects/next-app-16-2-6/src/app/globals.css +0 -26
  154. package/test/nextjs-test-projects/next-app-16-2-6/src/app/layout.tsx +0 -59
  155. package/test/nextjs-test-projects/next-app-16-2-6/src/app/page.tsx +0 -755
  156. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  157. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  158. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  159. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  160. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  161. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  162. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  163. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  164. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  165. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  166. package/test/nextjs-test-projects/next-app-16-2-6/src/app/revalidation-interface.tsx +0 -267
  167. package/test/nextjs-test-projects/next-app-16-2-6/src/app/update-tag-test/page.tsx +0 -25
  168. package/test/nextjs-test-projects/next-app-16-2-6/tsconfig.json +0 -34
  169. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/README.md +0 -36
  170. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +0 -3
  171. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/eslint.config.mjs +0 -18
  172. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +0 -15
  173. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/package.json +0 -28
  174. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/pnpm-lock.yaml +0 -4165
  175. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/postcss.config.mjs +0 -7
  176. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/file.svg +0 -1
  177. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/globe.svg +0 -1
  178. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/next.svg +0 -1
  179. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/vercel.svg +0 -1
  180. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/window.svg +0 -1
  181. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  182. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  183. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  184. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  185. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  186. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  187. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  188. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  189. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +0 -112
  190. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  191. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  192. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  193. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  194. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  195. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  196. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/favicon.ico +0 -0
  197. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/globals.css +0 -26
  198. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/layout.tsx +0 -57
  199. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/page.tsx +0 -755
  200. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/revalidation-interface.tsx +0 -267
  201. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +0 -22
  202. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/tsconfig.json +0 -34
  203. package/test/nextjs-test-projects/next-app-16-3-0/README.md +0 -36
  204. package/test/nextjs-test-projects/next-app-16-3-0/eslint.config.mjs +0 -18
  205. package/test/nextjs-test-projects/next-app-16-3-0/next.config.ts +0 -7
  206. package/test/nextjs-test-projects/next-app-16-3-0/package.json +0 -28
  207. package/test/nextjs-test-projects/next-app-16-3-0/pnpm-lock.yaml +0 -4284
  208. package/test/nextjs-test-projects/next-app-16-3-0/postcss.config.mjs +0 -7
  209. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/cached-static-fetch/route.ts +0 -18
  210. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  211. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidatePath/route.ts +0 -15
  212. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidateTag/route.ts +0 -20
  213. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidated-fetch/route.ts +0 -17
  214. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/uncached-fetch/route.ts +0 -15
  215. package/test/nextjs-test-projects/next-app-16-3-0/src/app/favicon.ico +0 -0
  216. package/test/nextjs-test-projects/next-app-16-3-0/src/app/globals.css +0 -26
  217. package/test/nextjs-test-projects/next-app-16-3-0/src/app/layout.tsx +0 -59
  218. package/test/nextjs-test-projects/next-app-16-3-0/src/app/page.tsx +0 -755
  219. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  220. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  221. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  222. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  223. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  224. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  225. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  226. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  227. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  228. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  229. package/test/nextjs-test-projects/next-app-16-3-0/src/app/revalidation-interface.tsx +0 -267
  230. package/test/nextjs-test-projects/next-app-16-3-0/src/app/update-tag-test/page.tsx +0 -25
  231. package/test/nextjs-test-projects/next-app-16-3-0/tsconfig.json +0 -34
  232. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/README.md +0 -36
  233. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/cache-handler.js +0 -3
  234. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/eslint.config.mjs +0 -18
  235. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/next.config.ts +0 -15
  236. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/package.json +0 -28
  237. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/pnpm-lock.yaml +0 -4284
  238. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/postcss.config.mjs +0 -7
  239. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/file.svg +0 -1
  240. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/globe.svg +0 -1
  241. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/next.svg +0 -1
  242. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/vercel.svg +0 -1
  243. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/window.svg +0 -1
  244. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  245. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  246. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  247. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/expire-matrix/revalidate/route.ts +0 -34
  248. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/expire-matrix/route.ts +0 -23
  249. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  250. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  251. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  252. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/page.tsx +0 -112
  253. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/revalidate-durations/page.tsx +0 -106
  254. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  255. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  256. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  257. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  258. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-remote/page.tsx +0 -85
  259. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/favicon.ico +0 -0
  260. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/globals.css +0 -26
  261. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/layout.tsx +0 -57
  262. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/page.tsx +0 -755
  263. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/revalidation-interface.tsx +0 -267
  264. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/update-tag-test/page.tsx +0 -22
  265. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/tsconfig.json +0 -34
  266. package/test/nextjs-test-projects/next-app-customized/README.md +0 -36
  267. package/test/nextjs-test-projects/next-app-customized/customized-cache-handler.js +0 -34
  268. package/test/nextjs-test-projects/next-app-customized/eslint.config.mjs +0 -16
  269. package/test/nextjs-test-projects/next-app-customized/next.config.js +0 -6
  270. package/test/nextjs-test-projects/next-pages-16-2-6/README.md +0 -16
  271. package/test/nextjs-test-projects/next-pages-16-2-6/eslint.config.mjs +0 -18
  272. package/test/nextjs-test-projects/next-pages-16-2-6/next.config.ts +0 -7
  273. package/test/nextjs-test-projects/next-pages-16-2-6/package.json +0 -26
  274. package/test/nextjs-test-projects/next-pages-16-2-6/pnpm-lock.yaml +0 -3896
  275. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/api/revalidate.ts +0 -24
  276. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/index.tsx +0 -11
  277. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/isr/[slug].tsx +0 -49
  278. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/static-forever.tsx +0 -20
  279. package/test/nextjs-test-projects/next-pages-16-2-6/tsconfig.json +0 -34
  280. package/test/nextjs-test-projects/next-pages-16-3-0/README.md +0 -16
  281. package/test/nextjs-test-projects/next-pages-16-3-0/eslint.config.mjs +0 -18
  282. package/test/nextjs-test-projects/next-pages-16-3-0/next.config.ts +0 -7
  283. package/test/nextjs-test-projects/next-pages-16-3-0/package.json +0 -26
  284. package/test/nextjs-test-projects/next-pages-16-3-0/pnpm-lock.yaml +0 -3939
  285. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/api/revalidate.ts +0 -24
  286. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/index.tsx +0 -11
  287. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/isr/[slug].tsx +0 -49
  288. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/static-forever.tsx +0 -20
  289. package/test/nextjs-test-projects/next-pages-16-3-0/tsconfig.json +0 -34
  290. package/test/playwright/cache-lab.spec.ts +0 -232
  291. package/test/playwright/update-tag.spec.ts +0 -33
  292. package/test/vitest/integration/build-id-prefix.integration.test.ts +0 -102
  293. package/test/vitest/integration/cache-components/cache-components.integration.test.ts +0 -389
  294. package/test/vitest/integration/cache-components/redis-kill-reconnect.test.ts +0 -65
  295. package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +0 -59
  296. package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +0 -100
  297. package/test/vitest/integration/cache-components/scripts/redis-kill-reconnect.ts +0 -243
  298. package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +0 -113
  299. package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +0 -379
  300. package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +0 -155
  301. package/test/vitest/integration/nextjs-cache-handler.integration.test.ts +0 -959
  302. package/test/vitest/integration/pages-router.integration.test.ts +0 -430
  303. package/test/vitest/unit/SyncedMap-orphan-cleanup.test.ts +0 -111
  304. package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +0 -154
  305. package/test/vitest/unit/cache-components-update-tags.test.ts +0 -350
  306. package/test/vitest/unit/cache-ttl.test.ts +0 -67
  307. package/test/vitest/unit/compare-and-unlink.test.ts +0 -22
  308. package/test/vitest/unit/index.test.ts +0 -190
  309. package/test/vitest/unit/pages-router-kinds.test.ts +0 -303
  310. package/test/vitest/unit/reconnect-socket-already-opened.test.ts +0 -117
  311. package/test/vitest/unit/redis-connection.test.ts +0 -39
  312. package/test/vitest/unit/serializer.test.ts +0 -182
  313. package/test/vitest/unit/tag-revalidation.test.ts +0 -178
  314. package/test/vitest/unit/utils/prefix.test.ts +0 -115
  315. package/tsconfig.json +0 -18
  316. package/tsup.config.ts +0 -14
  317. package/vite.config.ts +0 -34
  318. package/vitest.cache-components.config.ts +0 -16
@@ -1,350 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
-
3
- const hoisted = vi.hoisted(() => {
4
- const store = new Map<string, string>();
5
- const unlinkedKeys: string[] = [];
6
- return { store, unlinkedKeys };
7
- });
8
-
9
- vi.mock('redis', () => {
10
- const createClient = () => {
11
- const client = {
12
- isReady: true,
13
- isOpen: true,
14
- on: vi.fn(),
15
- connect: vi.fn(async () => undefined),
16
- disconnect: vi.fn(),
17
- quit: vi.fn(),
18
- duplicate: vi.fn(() => ({
19
- connect: vi.fn(async () => undefined),
20
- subscribe: vi.fn(async () => undefined),
21
- on: vi.fn(),
22
- quit: vi.fn(async () => undefined),
23
- configGet: vi.fn(async () => ({ 'notify-keyspace-events': 'Exe' })),
24
- isOpen: true,
25
- isReady: true,
26
- })),
27
- get: vi.fn(
28
- async (_opts: unknown, key: string) => hoisted.store.get(key) ?? null,
29
- ),
30
- hScan: vi.fn(async () => ({ cursor: 0, tuples: [] })),
31
- scan: vi.fn(async () => ({ cursor: 0, keys: [] })),
32
- hSet: vi.fn(async () => 1),
33
- hDel: vi.fn(async () => 1),
34
- publish: vi.fn(async () => 1),
35
- unlink: vi.fn(async (keys: string | string[]) => {
36
- const list = Array.isArray(keys) ? keys : [keys];
37
- for (const key of list) {
38
- hoisted.unlinkedKeys.push(key);
39
- hoisted.store.delete(key);
40
- }
41
- return list.length;
42
- }),
43
- eval: vi.fn(
44
- async (
45
- _script: string,
46
- options: { keys: string[]; arguments: string[] },
47
- ) => {
48
- const key = options.keys[0];
49
- const expected = options.arguments[0];
50
- if (hoisted.store.get(key) === expected) {
51
- hoisted.unlinkedKeys.push(key);
52
- hoisted.store.delete(key);
53
- return 1;
54
- }
55
- return 0;
56
- },
57
- ),
58
- set: vi.fn(async (key: string, value: string) => {
59
- hoisted.store.set(key, value);
60
- return 'OK';
61
- }),
62
- };
63
- return client;
64
- };
65
-
66
- return {
67
- createClient,
68
- commandOptions: vi.fn((opts) => opts),
69
- };
70
- });
71
-
72
- function cachedEntry(tags: string[], timestamp: number) {
73
- return {
74
- value: new ReadableStream<Uint8Array>({
75
- start(controller) {
76
- controller.enqueue(new TextEncoder().encode('cached'));
77
- controller.close();
78
- },
79
- }),
80
- tags,
81
- stale: 1,
82
- timestamp,
83
- expire: 3600,
84
- revalidate: 60,
85
- };
86
- }
87
-
88
- describe('RedisCacheComponentsHandler.updateTags vs Next.js default handler', () => {
89
- beforeEach(() => {
90
- vi.useFakeTimers();
91
- hoisted.store.clear();
92
- hoisted.unlinkedKeys.length = 0;
93
- vi.resetModules();
94
- });
95
-
96
- afterEach(() => {
97
- vi.useRealTimers();
98
- });
99
-
100
- async function createHandler(keyPrefix: string) {
101
- const { getRedisCacheComponentsHandler } = await import(
102
- '../../../src/CacheComponentsHandler'
103
- );
104
-
105
- return getRedisCacheComponentsHandler({
106
- keyPrefix,
107
- redisGetDeduplication: false,
108
- });
109
- }
110
-
111
- it('SWR expire keeps the entry and sets revalidate=-1 until the window elapses', async () => {
112
- const handler = await createHandler('swr:');
113
- const createdAt = Date.now();
114
-
115
- await handler.set(
116
- 'page',
117
- Promise.resolve(cachedEntry(['posts'], createdAt)),
118
- );
119
- // Next.js areTagsStale/areTagsExpired require stale/expired > entry.timestamp.
120
- await vi.advanceTimersByTimeAsync(1);
121
- const revalidatedAt = Date.now();
122
- await handler.updateTags(['posts'], { expire: 2 });
123
-
124
- expect(await handler.getExpiration(['posts'])).toBe(revalidatedAt + 2_000);
125
-
126
- const served = await handler.get('page', []);
127
- expect(served).toBeDefined();
128
- expect(served?.revalidate).toBe(-1);
129
- expect(hoisted.unlinkedKeys).toEqual([]);
130
-
131
- await vi.advanceTimersByTimeAsync(2_000);
132
- expect(await handler.get('page', [])).toBeUndefined();
133
- });
134
-
135
- it('cacheLife max expire (~1y) is SWR, not a delay before staleness', async () => {
136
- const handler = await createHandler('max:');
137
- const createdAt = Date.now();
138
- const oneYear = 60 * 60 * 24 * 365;
139
-
140
- await handler.set(
141
- 'page',
142
- Promise.resolve(cachedEntry(['posts'], createdAt)),
143
- );
144
- await vi.advanceTimersByTimeAsync(1);
145
- const revalidatedAt = Date.now();
146
- await handler.updateTags(['posts'], { expire: oneYear });
147
-
148
- expect(await handler.getExpiration(['posts'])).toBe(
149
- revalidatedAt + oneYear * 1000,
150
- );
151
- const served = await handler.get('page', []);
152
- expect(served?.revalidate).toBe(-1);
153
- expect(await handler.get('page', [])).toBeDefined();
154
- });
155
-
156
- it('omitted durations is a blocking miss (updateTag / deprecated revalidateTag)', async () => {
157
- const handler = await createHandler('hard:');
158
- const createdAt = Date.now();
159
-
160
- await handler.set(
161
- 'page',
162
- Promise.resolve(cachedEntry(['posts'], createdAt)),
163
- );
164
- await vi.advanceTimersByTimeAsync(1);
165
- const revalidatedAt = Date.now();
166
- await handler.updateTags(['posts']);
167
-
168
- expect(await handler.getExpiration(['posts'])).toBe(revalidatedAt);
169
- expect((handler as any).revalidatedTagsMap.get('posts')).toBe(
170
- revalidatedAt,
171
- );
172
- expect(await handler.get('page', [])).toBeUndefined();
173
- });
174
-
175
- it('{ expire: 0 } is a blocking miss', async () => {
176
- const handler = await createHandler('zero:');
177
- const createdAt = Date.now();
178
-
179
- await handler.set(
180
- 'page',
181
- Promise.resolve(cachedEntry(['posts'], createdAt)),
182
- );
183
- await vi.advanceTimersByTimeAsync(1);
184
- const revalidatedAt = Date.now();
185
- await handler.updateTags(['posts'], { expire: 0 });
186
-
187
- expect(await handler.getExpiration(['posts'])).toBe(revalidatedAt);
188
- expect((handler as any).revalidatedTagsMap.get('posts')).toBe(
189
- revalidatedAt,
190
- );
191
- expect(await handler.get('page', [])).toBeUndefined();
192
- });
193
-
194
- it('persists stale+expired so a later getExpiration still sees the window', async () => {
195
- const handler = await createHandler('persist:');
196
- const startedAt = Date.now();
197
-
198
- await handler.updateTags(['posts'], { expire: 2 });
199
- const stored = (handler as any).revalidatedTagsMap.get('posts');
200
-
201
- expect(stored).toEqual({
202
- stale: startedAt,
203
- expired: startedAt + 2_000,
204
- });
205
- });
206
-
207
- it('legacy plain number in Redis hard-expires once the timestamp is in the past', async () => {
208
- const handler = await createHandler('legacy-past:');
209
- const createdAt = Date.now() - 10_000;
210
-
211
- await handler.set(
212
- 'page',
213
- Promise.resolve(cachedEntry(['posts'], createdAt)),
214
- );
215
-
216
- // Older package versions stored Date.now() directly (not Next.js).
217
- (handler as any).revalidatedTagsMap.set('posts', Date.now() - 5_000);
218
-
219
- expect(await handler.get('page', [])).toBeUndefined();
220
- });
221
-
222
- it('legacy plain number ahead of local clock is clamped and hard-expires immediately', async () => {
223
- vi.setSystemTime(new Date(1_000_000));
224
- const handler = await createHandler('legacy-skew:');
225
- const createdAt = 900_000;
226
-
227
- await handler.set(
228
- 'page',
229
- Promise.resolve(cachedEntry(['posts'], createdAt)),
230
- );
231
-
232
- // Another instance with a faster clock wrote this value to Redis.
233
- (handler as any).revalidatedTagsMap.set('posts', 1_005_000);
234
-
235
- expect(await handler.get('page', [])).toBeUndefined();
236
- });
237
-
238
- it.each([
239
- ['seconds', 60],
240
- ['minutes', 60 * 60],
241
- ['hours', 60 * 60 * 24],
242
- ['days', 60 * 60 * 24 * 7],
243
- ['weeks', 60 * 60 * 24 * 30],
244
- ['max', 60 * 60 * 24 * 365],
245
- ['default', 0xfffffffe],
246
- ] as const)(
247
- 'cacheLife %s expire writes getExpiration=now+expire*1000 and SWR',
248
- async (_profile, expireSeconds) => {
249
- const handler = await createHandler(`preset-${_profile}:`);
250
- const createdAt = Date.now();
251
-
252
- await handler.set(
253
- 'page',
254
- Promise.resolve(cachedEntry(['posts'], createdAt)),
255
- );
256
- await vi.advanceTimersByTimeAsync(1);
257
- const revalidatedAt = Date.now();
258
- await handler.updateTags(['posts'], { expire: expireSeconds });
259
-
260
- expect(await handler.getExpiration(['posts'])).toBe(
261
- revalidatedAt + expireSeconds * 1000,
262
- );
263
- const served = await handler.get('page', []);
264
- expect(served?.revalidate).toBe(-1);
265
- expect(await handler.get('page', [])).toBeDefined();
266
- },
267
- );
268
-
269
- it('does not UNLINK a Redis replacement after a stale deduped read', async () => {
270
- const { getRedisCacheComponentsHandler } = await import(
271
- '../../../src/CacheComponentsHandler'
272
- );
273
- const handler = getRedisCacheComponentsHandler({
274
- keyPrefix: 'dedup-unlink:',
275
- redisGetDeduplication: true,
276
- inMemoryCachingTime: 10_000,
277
- });
278
-
279
- const createdAt = Date.now();
280
- await handler.set(
281
- 'page',
282
- Promise.resolve(cachedEntry(['posts'], createdAt)),
283
- );
284
- const redisKey = 'dedup-unlink:page';
285
- const oldSerialized = hoisted.store.get(redisKey)!;
286
-
287
- await vi.advanceTimersByTimeAsync(1);
288
- await handler.updateTags(['posts']);
289
-
290
- const freshAt = Date.now();
291
- hoisted.store.set(
292
- redisKey,
293
- JSON.stringify({
294
- value: Buffer.from('fresh').toString('base64'),
295
- tags: ['posts'],
296
- stale: 1,
297
- timestamp: freshAt,
298
- expire: 3600,
299
- revalidate: 60,
300
- }),
301
- );
302
- (handler as any).redisDeduplicationHandler.seedRequestReturn(
303
- 'page',
304
- oldSerialized,
305
- );
306
-
307
- hoisted.unlinkedKeys.length = 0;
308
- const served = await handler.get('page', []);
309
-
310
- expect(hoisted.unlinkedKeys).toEqual([]);
311
- expect(served).toBeDefined();
312
- expect(hoisted.store.get(redisKey)).toBeTruthy();
313
- });
314
-
315
- it('keeps a later stale-only update after a hard expire and replacement', async () => {
316
- const handler = await createHandler('stale-after-hard:');
317
- const createdAt = Date.now();
318
-
319
- await handler.set(
320
- 'page',
321
- Promise.resolve(cachedEntry(['posts'], createdAt)),
322
- );
323
- await vi.advanceTimersByTimeAsync(1);
324
- const hardExpiredAt = Date.now();
325
- await handler.updateTags(['posts']);
326
- expect((handler as any).revalidatedTagsMap.get('posts')).toBe(
327
- hardExpiredAt,
328
- );
329
-
330
- await vi.advanceTimersByTimeAsync(1);
331
- const replacementAt = Date.now();
332
- await handler.set(
333
- 'page',
334
- Promise.resolve(cachedEntry(['posts'], replacementAt)),
335
- );
336
-
337
- await vi.advanceTimersByTimeAsync(1);
338
- const staleAt = Date.now();
339
- await handler.updateTags(['posts'], {});
340
-
341
- expect((handler as any).revalidatedTagsMap.get('posts')).toEqual({
342
- stale: staleAt,
343
- expired: hardExpiredAt,
344
- });
345
-
346
- const served = await handler.get('page', []);
347
- expect(served).toBeDefined();
348
- expect(served?.revalidate).toBe(-1);
349
- });
350
- });
@@ -1,67 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { resolveCacheEntryTtlSeconds } from '../../../src/utils/cacheTtl';
3
-
4
- const estimateExpireAge = (staleAge: number) => staleAge * 1.2;
5
- const defaultStaleAge = 60 * 60 * 24 * 14;
6
-
7
- describe('resolveCacheEntryTtlSeconds', () => {
8
- it('prefers cacheControl.expire over revalidate (SWR-safe TTL)', () => {
9
- const ttl = resolveCacheEntryTtlSeconds(
10
- { cacheControl: { revalidate: 60, expire: 3600 } },
11
- { kind: 'APP_PAGE' },
12
- { estimateExpireAge, defaultStaleAge },
13
- );
14
-
15
- expect(ttl).toBe(3600);
16
- });
17
-
18
- it('falls back to estimateExpireAge(revalidate) when expire is omitted', () => {
19
- const ttl = resolveCacheEntryTtlSeconds(
20
- { cacheControl: { revalidate: 60, expire: undefined } },
21
- { kind: 'PAGES' },
22
- { estimateExpireAge, defaultStaleAge },
23
- );
24
-
25
- expect(ttl).toBe(72);
26
- });
27
-
28
- it('uses FETCH data.revalidate when cacheControl is absent', () => {
29
- const ttl = resolveCacheEntryTtlSeconds(
30
- {},
31
- { kind: 'FETCH', revalidate: 30 },
32
- { estimateExpireAge, defaultStaleAge },
33
- );
34
-
35
- expect(ttl).toBe(36);
36
- });
37
-
38
- it('uses defaultStaleAge when revalidate is false', () => {
39
- const ttl = resolveCacheEntryTtlSeconds(
40
- { cacheControl: { revalidate: false, expire: undefined } },
41
- { kind: 'PAGES' },
42
- { estimateExpireAge, defaultStaleAge },
43
- );
44
-
45
- expect(ttl).toBe(estimateExpireAge(defaultStaleAge));
46
- });
47
-
48
- it('returns undefined when no expire or revalidate is provided', () => {
49
- const ttl = resolveCacheEntryTtlSeconds(
50
- { cacheControl: { revalidate: undefined, expire: undefined } },
51
- { kind: 'APP_ROUTE' },
52
- { estimateExpireAge, defaultStaleAge },
53
- );
54
-
55
- expect(ttl).toBeUndefined();
56
- });
57
-
58
- it('supports legacy ctx.revalidate (Next.js 15.0.3)', () => {
59
- const ttl = resolveCacheEntryTtlSeconds(
60
- { revalidate: 60 },
61
- { kind: 'PAGES' },
62
- { estimateExpireAge, defaultStaleAge },
63
- );
64
-
65
- expect(ttl).toBe(72);
66
- });
67
- });
@@ -1,22 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { compareAndUnlink } from '../../../src/utils/compareAndUnlink';
3
-
4
- describe('compareAndUnlink', () => {
5
- it('evals GET+UNLINK and reports whether the key was removed', async () => {
6
- const evalFn = vi.fn(async () => 1);
7
- await expect(compareAndUnlink({ eval: evalFn }, 'k', 'old')).resolves.toBe(
8
- true,
9
- );
10
- expect(evalFn).toHaveBeenCalledWith(
11
- expect.stringContaining("redis.call('UNLINK'"),
12
- { keys: ['k'], arguments: ['old'] },
13
- );
14
- });
15
-
16
- it('returns false when the value no longer matches', async () => {
17
- const evalFn = vi.fn(async () => 0);
18
- await expect(compareAndUnlink({ eval: evalFn }, 'k', 'old')).resolves.toBe(
19
- false,
20
- );
21
- });
22
- });
@@ -1,190 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
-
3
- import type { CreateRedisStringsHandlerOptions } from '../../../src/index';
4
- import RedisStringsHandler from '../../../src/RedisStringsHandler';
5
-
6
- vi.mock('redis', () => {
7
- const createClient = vi.fn(() => {
8
- return {
9
- isReady: true,
10
- on: vi.fn(),
11
- connect: vi.fn(async () => undefined),
12
- disconnect: vi.fn(),
13
- quit: vi.fn(),
14
- duplicate: vi.fn(() => ({
15
- connect: vi.fn(async () => undefined),
16
- subscribe: vi.fn(async () => undefined),
17
- on: vi.fn(),
18
- quit: vi.fn(async () => undefined),
19
- configGet: vi.fn(async () => ({ 'notify-keyspace-events': 'Exe' })),
20
- })),
21
- get: vi.fn(async () => {
22
- const err = new Error('timeout') as Error & { name: string };
23
- err.name = 'AbortError';
24
- throw err;
25
- }),
26
- hScan: vi.fn(async () => ({ cursor: 0, tuples: [] })),
27
- scan: vi.fn(async () => ({ cursor: 0, keys: [] })),
28
- hSet: vi.fn(async () => 1),
29
- hDel: vi.fn(async () => 1),
30
- publish: vi.fn(async () => 1),
31
- unlink: vi.fn(async () => 1),
32
- set: vi.fn(async () => 'OK'),
33
- };
34
- });
35
- return {
36
- createClient,
37
- commandOptions: vi.fn((opts) => opts),
38
- };
39
- });
40
-
41
- vi.mock('../../src/SyncedMap', () => {
42
- class SyncedMap {
43
- waitUntilReady = vi.fn(async () => undefined);
44
- get = vi.fn(() => undefined);
45
- set = vi.fn(async () => undefined);
46
- delete = vi.fn(async () => undefined);
47
- entries = vi.fn(function* () {
48
- return;
49
- });
50
-
51
- constructor() {}
52
- }
53
-
54
- return { SyncedMap };
55
- });
56
-
57
- describe('RedisStringsHandler', () => {
58
- beforeEach(() => {
59
- vi.restoreAllMocks();
60
- });
61
-
62
- afterEach(() => {
63
- vi.unstubAllEnvs();
64
- });
65
-
66
- it('treats aborted GET (timeout) as cache miss without console.error', async () => {
67
- const consoleErrorSpy = vi
68
- .spyOn(console, 'error')
69
- .mockImplementation(() => undefined);
70
-
71
- const handler = new RedisStringsHandler({
72
- redisUrl: 'redis://localhost:6379',
73
- keyPrefix: 'test:',
74
- database: 0,
75
- getTimeoutMs: 1,
76
- redisGetDeduplication: false,
77
- });
78
-
79
- const res = await handler.get('missing-key', {
80
- kind: 'APP_PAGE',
81
- isRoutePPREnabled: false,
82
- isFallback: false,
83
- });
84
-
85
- expect(res).toBeNull();
86
- expect(consoleErrorSpy).not.toHaveBeenCalled();
87
- });
88
-
89
- it('does not leave the readiness timeout timer pending after an operation', async () => {
90
- vi.useFakeTimers();
91
- try {
92
- const handler = new RedisStringsHandler({
93
- redisUrl: 'redis://localhost:6379',
94
- keyPrefix: 'test:',
95
- database: 0,
96
- getTimeoutMs: 1,
97
- redisGetDeduplication: false,
98
- });
99
-
100
- const timersBefore = vi.getTimerCount();
101
- await handler.get('missing-key', {
102
- kind: 'APP_PAGE',
103
- isRoutePPREnabled: false,
104
- isFallback: false,
105
- });
106
-
107
- // assertClientIsReady() races waitUntilReady() against a 30s timeout;
108
- // the timer must not stay pending once the race is settled, because
109
- // pending timers retain the ambient async context (and with it, in a
110
- // Next.js server, the whole per-request object graph).
111
- expect(vi.getTimerCount()).toBe(timersBefore);
112
- } finally {
113
- vi.useRealTimers();
114
- }
115
- });
116
-
117
- it('skips Redis work in revalidateTag during next build', async () => {
118
- vi.stubEnv('NEXT_PHASE', 'phase-production-build');
119
- const { default: Handler } = await import(
120
- '../../../src/RedisStringsHandler'
121
- );
122
- const handler = new Handler({
123
- redisUrl: 'redis://localhost:6379',
124
- keyPrefix: 'build:',
125
- database: 0,
126
- redisGetDeduplication: false,
127
- });
128
- const unlink = (handler as any).client.unlink as ReturnType<typeof vi.fn>;
129
-
130
- await expect(handler.revalidateTag('posts')).resolves.toBeUndefined();
131
- expect(unlink).not.toHaveBeenCalled();
132
- });
133
- });
134
-
135
- describe('Public exports', () => {
136
- it('exports CreateRedisStringsHandlerOptions type', () => {
137
- const _typeCheck: CreateRedisStringsHandlerOptions = {
138
- keyPrefix: 'test',
139
- };
140
-
141
- expect(_typeCheck.keyPrefix).toBe('test');
142
- });
143
- });
144
-
145
- describe('redisCacheHandler lazy proxy', () => {
146
- it('does not construct a Redis connection on import', async () => {
147
- vi.resetModules();
148
- const { createClient } = await import('redis');
149
- vi.mocked(createClient).mockClear();
150
-
151
- // Importing the module should NOT trigger a Redis connection
152
- await import('../../../src');
153
-
154
- expect(vi.mocked(createClient)).not.toHaveBeenCalled();
155
- });
156
-
157
- it('has trap returns true for known methods without constructing the handler', async () => {
158
- vi.resetModules();
159
- const { createClient } = await import('redis');
160
- vi.mocked(createClient).mockClear();
161
-
162
- const { redisCacheHandler } = await import('../../../src');
163
-
164
- expect('getExpiration' in redisCacheHandler).toBe(true);
165
- expect('get' in redisCacheHandler).toBe(true);
166
- expect('set' in redisCacheHandler).toBe(true);
167
- expect('refreshTags' in redisCacheHandler).toBe(true);
168
- expect('updateTags' in redisCacheHandler).toBe(true);
169
-
170
- expect(vi.mocked(createClient)).not.toHaveBeenCalled();
171
- });
172
-
173
- it('method call delegates to the singleton and binds correctly', async () => {
174
- vi.resetModules();
175
- const { createClient } = await import('redis');
176
- vi.mocked(createClient).mockClear();
177
-
178
- const { redisCacheHandler } = await import('../../../src');
179
-
180
- // Accessing a method triggers construction and delegates to the real handler
181
- const getFn = redisCacheHandler.get;
182
- expect(typeof getFn).toBe('function');
183
- expect(vi.mocked(createClient)).toHaveBeenCalledTimes(1);
184
-
185
- // Subsequent accesses reuse the same singleton (no extra construction)
186
- const getFn2 = redisCacheHandler.get;
187
- expect(vi.mocked(createClient)).toHaveBeenCalledTimes(1);
188
- expect(typeof getFn2).toBe('function');
189
- });
190
- });