@trieb.work/nextjs-turbo-redis-cache 1.15.0 → 1.16.0

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 (263) hide show
  1. package/.cursor/skills/testing/SKILL.md +121 -0
  2. package/.github/workflows/ci.yml +199 -69
  3. package/.github/workflows/release.yml +9 -1
  4. package/ARCHITECTURE.md +431 -0
  5. package/CHANGELOG.md +28 -0
  6. package/README.md +77 -49
  7. package/dist/index.d.mts +71 -45
  8. package/dist/index.d.ts +71 -45
  9. package/dist/index.js +107 -21
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +107 -21
  12. package/dist/index.mjs.map +1 -1
  13. package/docs/index.html +7 -6
  14. package/eslint.config.mjs +1 -1
  15. package/package.json +15 -16
  16. package/playwright.config.ts +5 -3
  17. package/src/CacheComponentsHandler.ts +57 -2
  18. package/src/RedisStringsHandler.ts +125 -66
  19. package/src/SyncedMap.ts +62 -13
  20. package/test/README.md +194 -0
  21. package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/package.json +6 -6
  22. package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-15-4-11}/pnpm-lock.yaml +89 -105
  23. package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/package.json +3 -3
  24. package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/pnpm-lock.yaml +53 -53
  25. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/package.json +28 -0
  26. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/pnpm-lock.yaml +4206 -0
  27. package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +18 -0
  28. package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/package.json +3 -3
  29. package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/pnpm-lock.yaml +54 -60
  30. package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +7 -0
  31. package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
  32. package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-2-6}/tsconfig.json +9 -2
  33. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +3 -0
  34. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +13 -0
  35. package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6-cache-components}/package.json +3 -3
  36. package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6-cache-components}/pnpm-lock.yaml +54 -60
  37. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +19 -0
  38. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +24 -0
  39. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +21 -0
  40. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +19 -0
  41. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +19 -0
  42. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +110 -0
  43. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +90 -0
  44. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +127 -0
  45. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +130 -0
  46. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +127 -0
  47. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +110 -0
  48. package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/layout.tsx +0 -2
  49. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +22 -0
  50. package/test/nextjs-test-projects/next-pages-16-2-6/README.md +16 -0
  51. package/test/nextjs-test-projects/next-pages-16-2-6/eslint.config.mjs +18 -0
  52. package/test/nextjs-test-projects/next-pages-16-2-6/next.config.ts +7 -0
  53. package/test/nextjs-test-projects/next-pages-16-2-6/package.json +26 -0
  54. package/test/nextjs-test-projects/next-pages-16-2-6/pnpm-lock.yaml +3896 -0
  55. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/api/revalidate.ts +24 -0
  56. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/index.tsx +11 -0
  57. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/isr/[slug].tsx +49 -0
  58. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/static-forever.tsx +20 -0
  59. package/test/nextjs-test-projects/next-pages-16-2-6/tsconfig.json +34 -0
  60. package/{tests → test/playwright}/update-tag.spec.ts +1 -1
  61. package/test/{integration → vitest/integration}/build-id-prefix.integration.test.ts +2 -2
  62. package/test/{cache-components/cache-components.integration.spec.ts → vitest/integration/cache-components/cache-components.integration.test.ts} +7 -3
  63. package/test/{cache-components/redis-kill-reconnect.spec.ts → vitest/integration/cache-components/redis-kill-reconnect.test.ts} +28 -7
  64. package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +59 -0
  65. package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +100 -0
  66. package/test/{cache-components → vitest/integration/cache-components}/scripts/redis-kill-reconnect.ts +31 -8
  67. package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +113 -0
  68. package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +379 -0
  69. package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +155 -0
  70. package/test/{integration → vitest/integration}/nextjs-cache-handler.integration.test.ts +15 -9
  71. package/test/vitest/integration/pages-router.integration.test.ts +420 -0
  72. package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +154 -0
  73. package/{src → test/vitest/unit}/index.test.ts +3 -3
  74. package/test/vitest/unit/pages-router-kinds.test.ts +292 -0
  75. package/test/{cache-components/reconnect-socket-already-opened.spec.ts → vitest/unit/reconnect-socket-already-opened.test.ts} +1 -1
  76. package/{src → test/vitest/unit}/serializer.test.ts +7 -3
  77. package/{src → test/vitest/unit}/utils/prefix.test.ts +1 -1
  78. package/vite.config.ts +4 -3
  79. package/vitest.cache-components.config.ts +1 -1
  80. package/test/browser/update-tag.browser.test.ts +0 -41
  81. package/test/integration/next-app-15-0-3/next.config.js +0 -6
  82. package/test/integration/next-app-15-0-3/package-lock.json +0 -5833
  83. package/test/integration/next-app-15-0-3/pnpm-lock.yaml +0 -3710
  84. package/test/integration/next-app-15-3-2/next.config.js +0 -6
  85. package/test/integration/next-app-15-3-2/package.json +0 -33
  86. package/test/integration/next-app-15-3-2/pnpm-lock.yaml +0 -3720
  87. package/test/integration/next-app-15-3-2/postcss.config.mjs +0 -5
  88. package/test/integration/next-app-15-3-2/src/app/api/revalidateTag/route.ts +0 -15
  89. package/test/integration/next-app-15-4-7/eslint.config.mjs +0 -16
  90. package/test/integration/next-app-15-4-7/package-lock.json +0 -5969
  91. package/test/integration/next-app-15-4-7/package.json +0 -33
  92. package/test/integration/next-app-15-4-7/postcss.config.mjs +0 -5
  93. package/test/integration/next-app-15-4-7/src/app/api/revalidateTag/route.ts +0 -15
  94. package/test/integration/next-app-15-4-7/tsconfig.json +0 -27
  95. package/test/integration/next-app-16-0-3/src/app/api/cached-static-fetch/route.ts +0 -18
  96. package/test/integration/next-app-16-0-3/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  97. package/test/integration/next-app-16-0-3/src/app/api/revalidatePath/route.ts +0 -15
  98. package/test/integration/next-app-16-0-3/src/app/api/revalidated-fetch/route.ts +0 -17
  99. package/test/integration/next-app-16-0-3/src/app/api/uncached-fetch/route.ts +0 -15
  100. package/test/integration/next-app-16-0-3/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  101. package/test/integration/next-app-16-0-3/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  102. package/test/integration/next-app-16-0-3/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  103. package/test/integration/next-app-16-0-3/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  104. package/test/integration/next-app-16-0-3/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  105. package/test/integration/next-app-16-0-3/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  106. package/test/integration/next-app-16-0-3/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  107. package/test/integration/next-app-16-0-3/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  108. package/test/integration/next-app-16-0-3/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  109. package/test/integration/next-app-16-0-3/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  110. package/test/integration/next-app-16-2-3/src/app/api/cached-static-fetch/route.ts +0 -18
  111. package/test/integration/next-app-16-2-3/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  112. package/test/integration/next-app-16-2-3/src/app/api/revalidatePath/route.ts +0 -15
  113. package/test/integration/next-app-16-2-3/src/app/api/revalidated-fetch/route.ts +0 -17
  114. package/test/integration/next-app-16-2-3/src/app/api/uncached-fetch/route.ts +0 -15
  115. package/test/integration/next-app-16-2-3/src/app/globals.css +0 -26
  116. package/test/integration/next-app-16-2-3/src/app/layout.tsx +0 -59
  117. package/test/integration/next-app-16-2-3/src/app/page.tsx +0 -755
  118. package/test/integration/next-app-16-2-3/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  119. package/test/integration/next-app-16-2-3/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  120. package/test/integration/next-app-16-2-3/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  121. package/test/integration/next-app-16-2-3/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  122. package/test/integration/next-app-16-2-3/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  123. package/test/integration/next-app-16-2-3/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  124. package/test/integration/next-app-16-2-3/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  125. package/test/integration/next-app-16-2-3/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  126. package/test/integration/next-app-16-2-3/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  127. package/test/integration/next-app-16-2-3/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  128. package/test/integration/next-app-16-2-3/src/app/revalidation-interface.tsx +0 -267
  129. package/test/integration/next-app-16-2-3-cache-components/public/file.svg +0 -1
  130. package/test/integration/next-app-16-2-3-cache-components/public/globe.svg +0 -1
  131. package/test/integration/next-app-16-2-3-cache-components/public/next.svg +0 -1
  132. package/test/integration/next-app-16-2-3-cache-components/public/vercel.svg +0 -1
  133. package/test/integration/next-app-16-2-3-cache-components/public/window.svg +0 -1
  134. package/test/integration/next-app-customized/README.md +0 -36
  135. package/test/integration/next-app-customized/eslint.config.mjs +0 -16
  136. package/vitest.browser.config.ts +0 -10
  137. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/README.md +0 -0
  138. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/eslint.config.mjs +0 -0
  139. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-15-4-11}/next.config.js +0 -0
  140. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-15-4-11}/package-lock.json +0 -0
  141. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/postcss.config.mjs +0 -0
  142. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/file.svg +0 -0
  143. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/globe.svg +0 -0
  144. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/next.svg +0 -0
  145. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/vercel.svg +0 -0
  146. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/window.svg +0 -0
  147. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/cached-static-fetch/route.ts +0 -0
  148. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -0
  149. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/revalidatePath/route.ts +0 -0
  150. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/revalidateTag/route.ts +0 -0
  151. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/revalidated-fetch/route.ts +0 -0
  152. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/uncached-fetch/route.ts +0 -0
  153. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/globals.css +0 -0
  154. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/layout.tsx +0 -0
  155. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/page.tsx +0 -0
  156. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -0
  157. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -0
  158. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  159. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/no-fetch/default-page/page.tsx +0 -0
  160. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -0
  161. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -0
  162. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  163. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -0
  164. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -0
  165. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  166. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/revalidation-interface.tsx +0 -0
  167. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/tsconfig.json +0 -0
  168. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/README.md +0 -0
  169. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/eslint.config.mjs +0 -0
  170. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/next.config.ts +0 -0
  171. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/postcss.config.mjs +0 -0
  172. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/cached-static-fetch/route.ts +0 -0
  173. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -0
  174. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/revalidatePath/route.ts +0 -0
  175. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/src/app/api/revalidateTag/route.ts +0 -0
  176. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/revalidated-fetch/route.ts +0 -0
  177. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/uncached-fetch/route.ts +0 -0
  178. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/src/app/favicon.ico +0 -0
  179. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/src/app/globals.css +0 -0
  180. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/layout.tsx +0 -0
  181. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/src/app/page.tsx +0 -0
  182. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -0
  183. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -0
  184. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  185. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/no-fetch/default-page/page.tsx +0 -0
  186. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -0
  187. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -0
  188. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  189. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -0
  190. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -0
  191. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  192. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/src/app/revalidation-interface.tsx +0 -0
  193. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/src/app/update-tag-test/page.tsx +0 -0
  194. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/tsconfig.json +0 -0
  195. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/README.md +0 -0
  196. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/cache-handler.js +0 -0
  197. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/eslint.config.mjs +0 -0
  198. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/next.config.ts +0 -0
  199. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/postcss.config.mjs +0 -0
  200. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/file.svg +0 -0
  201. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/globe.svg +0 -0
  202. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/next.svg +0 -0
  203. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/vercel.svg +0 -0
  204. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/window.svg +0 -0
  205. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/cached-static-fetch/route.ts +0 -0
  206. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/cached-with-cachelife/route.ts +0 -0
  207. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/cached-with-tag/route.ts +0 -0
  208. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/revalidate-tag/route.ts +0 -0
  209. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/revalidated-fetch/route.ts +0 -0
  210. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/cachelife-short/page.tsx +0 -0
  211. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/page.tsx +0 -0
  212. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -0
  213. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -0
  214. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/tag-invalidation/page.tsx +0 -0
  215. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -0
  216. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/favicon.ico +0 -0
  217. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/globals.css +0 -0
  218. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/layout.tsx +0 -0
  219. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/page.tsx +0 -0
  220. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/revalidation-interface.tsx +0 -0
  221. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/update-tag-test/page.tsx +0 -0
  222. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/tsconfig.json +0 -0
  223. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/README.md +0 -0
  224. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/next.config.ts +0 -0
  225. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/cached-static-fetch/route.ts +0 -0
  226. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -0
  227. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/revalidatePath/route.ts +0 -0
  228. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/src/app/api/revalidateTag/route.ts +0 -0
  229. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/revalidated-fetch/route.ts +0 -0
  230. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/uncached-fetch/route.ts +0 -0
  231. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/src/app/globals.css +0 -0
  232. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/layout.tsx +0 -0
  233. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/src/app/page.tsx +0 -0
  234. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -0
  235. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -0
  236. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  237. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/no-fetch/default-page/page.tsx +0 -0
  238. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -0
  239. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -0
  240. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  241. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -0
  242. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -0
  243. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  244. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/src/app/revalidation-interface.tsx +0 -0
  245. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/src/app/update-tag-test/page.tsx +0 -0
  246. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/README.md +0 -0
  247. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/eslint.config.mjs +0 -0
  248. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/postcss.config.mjs +0 -0
  249. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/file.svg +0 -0
  250. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/globe.svg +0 -0
  251. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/next.svg +0 -0
  252. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/vercel.svg +0 -0
  253. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/window.svg +0 -0
  254. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/favicon.ico +0 -0
  255. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/globals.css +0 -0
  256. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/page.tsx +0 -0
  257. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/revalidation-interface.tsx +0 -0
  258. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/tsconfig.json +0 -0
  259. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-customized}/README.md +0 -0
  260. /package/test/{integration → nextjs-test-projects}/next-app-customized/customized-cache-handler.js +0 -0
  261. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-customized}/eslint.config.mjs +0 -0
  262. /package/test/{integration → nextjs-test-projects}/next-app-customized/next.config.js +0 -0
  263. /package/{tests → test/playwright}/cache-lab.spec.ts +0 -0
@@ -0,0 +1,292 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+
3
+ import RedisStringsHandler, {
4
+ CacheEntry,
5
+ } from '../../../src/RedisStringsHandler';
6
+
7
+ // In-memory Redis string store shared between the mocked client and the tests
8
+ const hoisted = vi.hoisted(() => {
9
+ const store = new Map<string, { value: string; ex?: number }>();
10
+ return { store };
11
+ });
12
+
13
+ vi.mock('redis', () => {
14
+ const createClient = () => {
15
+ return {
16
+ isReady: true,
17
+ on: vi.fn(),
18
+ connect: vi.fn(async () => undefined),
19
+ disconnect: vi.fn(),
20
+ quit: vi.fn(),
21
+ duplicate: vi.fn(() => ({
22
+ connect: vi.fn(async () => undefined),
23
+ subscribe: vi.fn(async () => undefined),
24
+ on: vi.fn(),
25
+ quit: vi.fn(async () => undefined),
26
+ configGet: vi.fn(async () => ({ 'notify-keyspace-events': 'Exe' })),
27
+ })),
28
+ get: vi.fn(
29
+ async (_opts: unknown, key: string) =>
30
+ hoisted.store.get(key)?.value ?? null,
31
+ ),
32
+ hScan: vi.fn(async () => ({ cursor: 0, tuples: [] })),
33
+ scan: vi.fn(async () => ({ cursor: 0, keys: [] })),
34
+ hSet: vi.fn(async () => 1),
35
+ hDel: vi.fn(async () => 1),
36
+ publish: vi.fn(async () => 1),
37
+ unlink: vi.fn(async () => 1),
38
+ set: vi.fn(async (key: string, value: string, opts?: { EX?: number }) => {
39
+ hoisted.store.set(key, { value, ex: opts?.EX });
40
+ return 'OK';
41
+ }),
42
+ };
43
+ };
44
+ return {
45
+ createClient,
46
+ commandOptions: vi.fn((opts) => opts),
47
+ };
48
+ });
49
+
50
+ const KEY_PREFIX = 'test:';
51
+ const DEFAULT_STALE_AGE = 60 * 60 * 24 * 14;
52
+ // Default estimateExpireAge outside production is staleAge * 1.2
53
+ const expireAge = (staleAge: number) => staleAge * 1.2;
54
+
55
+ function createHandler() {
56
+ return new RedisStringsHandler({
57
+ redisUrl: 'redis://localhost:6379',
58
+ keyPrefix: KEY_PREFIX,
59
+ database: 0,
60
+ getTimeoutMs: 100,
61
+ redisGetDeduplication: false,
62
+ inMemoryCachingTime: 0,
63
+ });
64
+ }
65
+
66
+ function storedEntry(key: string): { entry: CacheEntry; ex?: number } {
67
+ const raw = hoisted.store.get(KEY_PREFIX + key);
68
+ expect(raw).toBeDefined();
69
+ return { entry: JSON.parse(raw!.value), ex: raw!.ex };
70
+ }
71
+
72
+ const baseCtx = { isRoutePPREnabled: false, isFallback: false };
73
+
74
+ describe('RedisStringsHandler Pages Router kinds', () => {
75
+ let warnSpy: ReturnType<typeof vi.spyOn>;
76
+ let errorSpy: ReturnType<typeof vi.spyOn>;
77
+
78
+ beforeEach(() => {
79
+ hoisted.store.clear();
80
+ warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
81
+ errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
82
+ });
83
+
84
+ afterEach(() => {
85
+ vi.restoreAllMocks();
86
+ });
87
+
88
+ describe('PAGES entries', () => {
89
+ const pagesData = {
90
+ kind: 'PAGES' as const,
91
+ html: '<html><body>hello</body></html>',
92
+ pageData: { pageProps: { title: 'hello' }, __N_SSG: true },
93
+ headers: undefined,
94
+ status: 200,
95
+ };
96
+
97
+ it('set() stores a PAGES entry without warnings and with the implicit path tag', async () => {
98
+ const handler = createHandler();
99
+ await handler.set('/blog/post-1', pagesData, {
100
+ ...baseCtx,
101
+ cacheControl: { revalidate: 60, expire: undefined },
102
+ });
103
+
104
+ const { entry, ex } = storedEntry('/blog/post-1');
105
+ expect(entry).toEqual({
106
+ value: pagesData,
107
+ lastModified: expect.any(Number),
108
+ tags: ['_N_T_/blog/post-1'],
109
+ });
110
+ // TTL is derived from cacheControl.revalidate (getStaticProps revalidate)
111
+ expect(ex).toBe(expireAge(60));
112
+ expect(warnSpy).not.toHaveBeenCalled();
113
+ expect(errorSpy).not.toHaveBeenCalled();
114
+ });
115
+
116
+ it('get() returns a PAGES entry without warnings', async () => {
117
+ const handler = createHandler();
118
+ await handler.set('/blog/post-1', pagesData, {
119
+ ...baseCtx,
120
+ cacheControl: { revalidate: 60, expire: undefined },
121
+ });
122
+
123
+ const result = await handler.get('/blog/post-1', {
124
+ kind: 'PAGES',
125
+ isFallback: false,
126
+ });
127
+
128
+ expect(result).toEqual({
129
+ value: pagesData,
130
+ lastModified: expect.any(Number),
131
+ tags: ['_N_T_/blog/post-1'],
132
+ });
133
+ expect(warnSpy).not.toHaveBeenCalled();
134
+ expect(errorSpy).not.toHaveBeenCalled();
135
+ });
136
+
137
+ it('set() with revalidate: false falls back to defaultStaleAge for the TTL', async () => {
138
+ const handler = createHandler();
139
+ await handler.set('/static-forever', pagesData, {
140
+ ...baseCtx,
141
+ cacheControl: { revalidate: false, expire: undefined },
142
+ });
143
+
144
+ const { ex } = storedEntry('/static-forever');
145
+ expect(ex).toBe(expireAge(DEFAULT_STALE_AGE));
146
+ });
147
+
148
+ it('set() derives the TTL from the legacy ctx.revalidate argument (Next.js 15.0.3)', async () => {
149
+ const handler = createHandler();
150
+ await handler.set('/blog/post-legacy', pagesData, {
151
+ ...baseCtx,
152
+ revalidate: 60,
153
+ });
154
+
155
+ const { ex } = storedEntry('/blog/post-legacy');
156
+ expect(ex).toBe(expireAge(60));
157
+ });
158
+ });
159
+
160
+ describe('REDIRECT entries (getStaticProps redirect)', () => {
161
+ const redirectData = {
162
+ kind: 'REDIRECT' as const,
163
+ // Next.js nests the redirect directives under `pageProps`, matching the
164
+ // real getStaticProps redirect payload asserted in the integration test.
165
+ props: {
166
+ pageProps: { __N_REDIRECT: '/target', __N_REDIRECT_STATUS: 307 },
167
+ },
168
+ };
169
+
170
+ it('set() and get() round-trip a REDIRECT entry without warnings', async () => {
171
+ const handler = createHandler();
172
+ await handler.set('/old-location', redirectData, {
173
+ ...baseCtx,
174
+ cacheControl: { revalidate: 30, expire: undefined },
175
+ });
176
+
177
+ const { entry, ex } = storedEntry('/old-location');
178
+ expect(entry).toEqual({
179
+ value: redirectData,
180
+ lastModified: expect.any(Number),
181
+ tags: ['_N_T_/old-location'],
182
+ });
183
+ expect(ex).toBe(expireAge(30));
184
+
185
+ const result = await handler.get('/old-location', {
186
+ kind: 'PAGES',
187
+ isFallback: false,
188
+ });
189
+ expect(result?.value).toEqual(redirectData);
190
+ expect(warnSpy).not.toHaveBeenCalled();
191
+ expect(errorSpy).not.toHaveBeenCalled();
192
+ });
193
+ });
194
+
195
+ describe('notFound entries (data === null)', () => {
196
+ it('set() stores a null-value entry without throwing or warning', async () => {
197
+ const handler = createHandler();
198
+ await handler.set('/missing-page', null, {
199
+ ...baseCtx,
200
+ cacheControl: { revalidate: 15, expire: undefined },
201
+ });
202
+
203
+ const { entry, ex } = storedEntry('/missing-page');
204
+ expect(entry).toEqual({
205
+ value: null,
206
+ lastModified: expect.any(Number),
207
+ tags: ['_N_T_/missing-page'],
208
+ });
209
+ expect(ex).toBe(expireAge(15));
210
+ expect(warnSpy).not.toHaveBeenCalled();
211
+ expect(errorSpy).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it('get() returns the null-value entry without malformed warnings or errors', async () => {
215
+ const handler = createHandler();
216
+ await handler.set('/missing-page', null, {
217
+ ...baseCtx,
218
+ cacheControl: { revalidate: 15, expire: undefined },
219
+ });
220
+
221
+ const result = await handler.get('/missing-page', {
222
+ kind: 'PAGES',
223
+ isFallback: false,
224
+ });
225
+
226
+ expect(result).toEqual({
227
+ value: null,
228
+ lastModified: expect.any(Number),
229
+ tags: ['_N_T_/missing-page'],
230
+ });
231
+ expect(warnSpy).not.toHaveBeenCalled();
232
+ expect(errorSpy).not.toHaveBeenCalled();
233
+ });
234
+
235
+ it('get() still warns for a malformed entry with a missing (undefined) value', async () => {
236
+ const handler = createHandler();
237
+ hoisted.store.set(KEY_PREFIX + '/malformed', {
238
+ value: JSON.stringify({ lastModified: Date.now(), tags: [] }),
239
+ });
240
+
241
+ await handler.get('/malformed', {
242
+ kind: 'PAGES',
243
+ isFallback: false,
244
+ });
245
+
246
+ expect(warnSpy).toHaveBeenCalledWith(
247
+ 'RedisStringsHandler.get() called with',
248
+ '/malformed',
249
+ expect.anything(),
250
+ 'cacheEntry is mall formed (missing value)',
251
+ );
252
+ });
253
+ });
254
+
255
+ describe('App Router entries are unaffected', () => {
256
+ it('set() still extracts tags from the x-next-cache-tags header and adds no implicit tag', async () => {
257
+ const handler = createHandler();
258
+ await handler.set(
259
+ '/app-page',
260
+ {
261
+ kind: 'APP_PAGE',
262
+ html: '<html></html>',
263
+ rscData: Buffer.from('rsc'),
264
+ headers: {
265
+ 'x-nextjs-stale-time': '1000',
266
+ 'x-next-cache-tags': '_N_T_/layout,_N_T_/app-page',
267
+ },
268
+ segmentData: undefined,
269
+ postboned: undefined,
270
+ },
271
+ { ...baseCtx, cacheControl: { revalidate: 60, expire: undefined } },
272
+ );
273
+
274
+ const { entry } = storedEntry('/app-page');
275
+ expect(entry.tags).toEqual(['_N_T_/layout', '_N_T_/app-page']);
276
+ expect(warnSpy).not.toHaveBeenCalled();
277
+ });
278
+
279
+ it('set() still warns for unsupported kinds (e.g. IMAGE)', async () => {
280
+ const handler = createHandler();
281
+ await handler.set(
282
+ '/some-image',
283
+ { kind: 'IMAGE' } as unknown as Parameters<
284
+ RedisStringsHandler['set']
285
+ >[1],
286
+ baseCtx,
287
+ );
288
+
289
+ expect(warnSpy).toHaveBeenCalled();
290
+ });
291
+ });
292
+ });
@@ -22,7 +22,7 @@ describe('RedisCacheComponentsHandler reconnect logic', () => {
22
22
  .spyOn(console, 'error')
23
23
  .mockImplementation(() => {});
24
24
 
25
- const { getRedisCacheComponentsHandler } = await import('../../src');
25
+ const { getRedisCacheComponentsHandler } = await import('../../../src');
26
26
 
27
27
  const handler = getRedisCacheComponentsHandler({
28
28
  redisUrl: 'redis://127.0.0.1:6399',
@@ -1,7 +1,10 @@
1
1
  import { describe, it, expect, vi } from 'vitest';
2
2
 
3
- import { CacheValueSerializer, jsonCacheValueSerializer } from './serializer';
4
- import type { CacheEntry } from './RedisStringsHandler';
3
+ import {
4
+ CacheValueSerializer,
5
+ jsonCacheValueSerializer,
6
+ } from '../../../src/serializer';
7
+ import type { CacheEntry } from '../../../src/RedisStringsHandler';
5
8
 
6
9
  function makeEntry(value: unknown): CacheEntry {
7
10
  return {
@@ -60,7 +63,8 @@ describe('jsonCacheValueSerializer (default)', () => {
60
63
  it('is referentially stable so consumers can detect default usage', async () => {
61
64
  // Importing twice should yield the same singleton; this guards against
62
65
  // accidental "factory" refactors that would break reference equality.
63
- const reimport = (await import('./serializer')).jsonCacheValueSerializer;
66
+ const reimport = (await import('../../../src/serializer'))
67
+ .jsonCacheValueSerializer;
64
68
  expect(reimport).toBe(jsonCacheValueSerializer);
65
69
  });
66
70
  });
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import os from 'node:os';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
- import { resolveKeyPrefix } from './prefix';
5
+ import { resolveKeyPrefix } from '../../../../src/utils/prefix';
6
6
 
7
7
  function withEnv<T>(env: Partial<NodeJS.ProcessEnv>, fn: () => T): T {
8
8
  const original: NodeJS.ProcessEnv = { ...process.env };
package/vite.config.ts CHANGED
@@ -5,9 +5,9 @@ export default defineConfig({
5
5
  globals: true,
6
6
  environment: 'node',
7
7
  include: [
8
- 'src/**/*.test.ts',
9
- 'src/**/*.test.tsx',
10
- 'test/integration/**/*.test.ts',
8
+ 'test/vitest/unit/**/*.test.ts',
9
+ 'test/vitest/unit/**/*.test.tsx',
10
+ 'test/vitest/integration/**/*.test.ts',
11
11
  ],
12
12
  exclude: [
13
13
  'node_modules',
@@ -15,6 +15,7 @@ export default defineConfig({
15
15
  '.git',
16
16
  '**/node_modules/**',
17
17
  '**/node_modules',
18
+ 'test/vitest/integration/cache-components/**',
18
19
  ],
19
20
  coverage: {
20
21
  provider: 'v8',
@@ -4,7 +4,7 @@ export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
6
  environment: 'node',
7
- include: ['test/cache-components/**/*.spec.ts'],
7
+ include: ['test/vitest/integration/cache-components/**/*.test.ts'],
8
8
  exclude: [
9
9
  'node_modules',
10
10
  'dist',
@@ -1,41 +0,0 @@
1
- import { test, expect } from 'vitest';
2
-
3
- // Base URL of the running Next 16 app.
4
- // Start it separately, e.g.:
5
- // cd test/integration/next-app-16-0-3 && pnpm dev
6
- // and ensure it listens on the same origin as below.
7
- const BASE_URL =
8
- (globalThis as any).NEXT_BROWSER_BASE_URL || 'http://localhost:3000';
9
-
10
- // This test exercises a real Server Action that calls updateTag.
11
- // It does not assert Redis state, but it verifies that calling the
12
- // server action endpoint does not trigger the "Server Action only" error.
13
-
14
- test('Server Action calling updateTag responds without server-action-only error', async () => {
15
- // 1) Fetch the SSR HTML for the page containing the Server Action form
16
- const res = await fetch(`${BASE_URL}/update-tag-test`);
17
- expect(res.status).toBeLessThan(500);
18
- const html = await res.text();
19
-
20
- // 2) Parse the HTML to find the form action URL that Next generated
21
- const parser = new DOMParser();
22
- const doc = parser.parseFromString(html, 'text/html');
23
- const form = doc.querySelector('form');
24
- expect(form).not.toBeNull();
25
-
26
- const actionAttr = form!.getAttribute('action');
27
- expect(actionAttr).toBeTruthy();
28
-
29
- const actionUrl = new URL(actionAttr!, BASE_URL).toString();
30
-
31
- // 3) Call the Server Action endpoint directly.
32
- // If updateTag were not allowed here, Next.js would respond with
33
- // an error page that contains the specific error message.
34
- const actionRes = await fetch(actionUrl, { method: 'POST' });
35
- const actionText = await actionRes.text();
36
-
37
- expect(actionRes.status).toBeLessThan(500);
38
- expect(actionText).not.toContain(
39
- 'updateTag can only be called from within a Server Action',
40
- );
41
- });
@@ -1,6 +0,0 @@
1
- /** @type {import('next').NextConfig} */
2
- const nextConfig = {
3
- cacheHandler: require.resolve('@trieb.work/nextjs-turbo-redis-cache'),
4
- };
5
-
6
- module.exports = nextConfig;