@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,389 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
- import { spawn, ChildProcess } from 'child_process';
3
- import { createClient, RedisClientType } from 'redis';
4
- import path from 'path';
5
-
6
- const PORT = Number(process.env.CACHE_COMPONENTS_PORT || '3065');
7
- const BASE_URL = `http://localhost:${PORT}`;
8
-
9
- describe('Next.js 16 Cache Components Integration', () => {
10
- let nextProcess: ChildProcess;
11
- let redisClient: RedisClientType;
12
- let keyPrefix: string;
13
-
14
- async function delay(ms: number) {
15
- return new Promise((resolve) => setTimeout(resolve, ms));
16
- }
17
-
18
- async function waitForRedisKeys(
19
- pattern: string,
20
- minCount = 1,
21
- timeoutMs = 10_000,
22
- ) {
23
- for (let elapsed = 0; elapsed < timeoutMs; elapsed += 100) {
24
- const keys = await redisClient.keys(pattern);
25
- if (keys.length >= minCount) {
26
- return keys;
27
- }
28
- await delay(100);
29
- }
30
- return redisClient.keys(pattern);
31
- }
32
-
33
- beforeAll(async () => {
34
- // Connect to Redis
35
- redisClient = createClient({
36
- url: process.env.REDIS_URL || 'redis://localhost:6379',
37
- database: 1,
38
- });
39
- await redisClient.connect();
40
-
41
- // Generate unique key prefix for this test run
42
- keyPrefix = `cache-components-test-${Math.random().toString(36).substring(7)}`;
43
- process.env.VERCEL_URL = keyPrefix;
44
-
45
- const cacheComponentsApp =
46
- process.env.CACHE_COMPONENTS_APP || 'next-app-16-2-6-cache-components';
47
-
48
- const appDir = path.join(
49
- __dirname,
50
- '..',
51
- '..',
52
- '..',
53
- 'nextjs-test-projects',
54
- cacheComponentsApp,
55
- );
56
-
57
- console.log('Installing Next.js app dependencies...');
58
- await new Promise<void>((resolve, reject) => {
59
- const installProcess = spawn('pnpm', ['install'], {
60
- cwd: appDir,
61
- stdio: 'inherit',
62
- });
63
-
64
- installProcess.on('close', (code) => {
65
- if (code === 0) resolve();
66
- else reject(new Error(`Install failed with code ${code}`));
67
- });
68
- });
69
-
70
- console.log('Building Next.js app...');
71
- await new Promise<void>((resolve, reject) => {
72
- const buildProcess = spawn('pnpm', ['build'], {
73
- cwd: appDir,
74
- stdio: 'inherit',
75
- });
76
-
77
- buildProcess.on('close', (code) => {
78
- if (code === 0) resolve();
79
- else reject(new Error(`Build failed with code ${code}`));
80
- });
81
- });
82
-
83
- console.log('Starting Next.js app...');
84
- nextProcess = spawn('pnpm', ['start', '-p', PORT.toString()], {
85
- cwd: appDir,
86
- env: { ...process.env, VERCEL_URL: keyPrefix },
87
- });
88
-
89
- // Wait for server to be ready
90
- await new Promise((resolve) => setTimeout(resolve, 3000));
91
- }, 120000);
92
-
93
- afterAll(async () => {
94
- // Clean up Redis keys
95
- const keys = await redisClient.keys(`${keyPrefix}*`);
96
- if (keys.length > 0) {
97
- await redisClient.del(keys);
98
- }
99
- await redisClient.quit();
100
-
101
- // Kill Next.js process
102
- if (nextProcess) {
103
- nextProcess.kill();
104
- }
105
- });
106
-
107
- describe('Basic use cache functionality', () => {
108
- it('should cache data and return same counter value on subsequent requests', async () => {
109
- // First request
110
- const res1 = await fetch(`${BASE_URL}/api/cached-static-fetch`);
111
- const data1 = await res1.json();
112
-
113
- expect(data1.counter).toBe(1);
114
-
115
- // Second request should return cached data
116
- const res2 = await fetch(`${BASE_URL}/api/cached-static-fetch`);
117
- const data2 = await res2.json();
118
-
119
- expect(data2.counter).toBe(1); // Same counter value
120
- expect(data2.timestamp).toBe(data1.timestamp); // Same timestamp
121
- });
122
-
123
- it('should store cache entry in Redis', async () => {
124
- // expire-matrix is dynamic (`ƒ`); static prerendered `use cache` routes may
125
- // not write through to Redis at runtime on some Next.js versions.
126
- await fetch(`${BASE_URL}/api/expire-matrix?id=redis-smoke`);
127
-
128
- const keys = await waitForRedisKeys(`${keyPrefix}*`);
129
- expect(keys.length).toBeGreaterThan(0);
130
- }, 15_000);
131
- });
132
-
133
- describe('cacheTag functionality', () => {
134
- it('should cache data with tags', async () => {
135
- const res1 = await fetch(`${BASE_URL}/api/cached-with-tag`);
136
- const data1 = await res1.json();
137
-
138
- expect(data1.counter).toBeDefined();
139
-
140
- // Second request should return cached data
141
- const res2 = await fetch(`${BASE_URL}/api/cached-with-tag`);
142
- const data2 = await res2.json();
143
-
144
- expect(data2.counter).toBe(data1.counter);
145
- });
146
-
147
- it('should invalidate cache when tag is revalidated', async () => {
148
- const id = `tag-invalidation-${Date.now()}`;
149
- const matrixUrl = `${BASE_URL}/api/expire-matrix?id=${encodeURIComponent(id)}`;
150
-
151
- const data1 = await (await fetch(matrixUrl)).json();
152
- const data2 = await (await fetch(matrixUrl)).json();
153
- expect(data2.counter).toBe(data1.counter);
154
-
155
- const revalidateRes = await fetch(
156
- `${BASE_URL}/api/expire-matrix/revalidate`,
157
- {
158
- method: 'POST',
159
- headers: { 'Content-Type': 'application/json' },
160
- body: JSON.stringify({
161
- tag: `expire-matrix-${id}`,
162
- profile: { expire: 0 },
163
- }),
164
- },
165
- );
166
- expect(revalidateRes.status).toBe(200);
167
-
168
- const after = await (await fetch(matrixUrl)).json();
169
- expect(after.counter).toBeGreaterThan(data1.counter);
170
- });
171
- });
172
-
173
- describe('cacheLife functionality', () => {
174
- it('should respect expire window and eventually return refreshed data', async () => {
175
- const res1 = await fetch(`${BASE_URL}/api/cached-with-cachelife`);
176
- const data1 = await res1.json();
177
-
178
- const res2 = await fetch(`${BASE_URL}/api/cached-with-cachelife`);
179
- const data2 = await res2.json();
180
- expect(data2.counter).toBe(data1.counter);
181
- expect(data2.timestamp).toBe(data1.timestamp);
182
-
183
- await delay(6500);
184
-
185
- let refreshedData: any;
186
- for (let i = 0; i < 10; i++) {
187
- const res = await fetch(`${BASE_URL}/api/cached-with-cachelife`);
188
- const data = await res.json();
189
-
190
- if (
191
- data.counter !== data1.counter ||
192
- data.timestamp !== data1.timestamp
193
- ) {
194
- refreshedData = data;
195
- break;
196
- }
197
-
198
- await delay(500);
199
- }
200
-
201
- expect(refreshedData).toBeDefined();
202
- expect(refreshedData.counter).toBeGreaterThan(data1.counter);
203
- expect(refreshedData.timestamp).not.toBe(data1.timestamp);
204
- }, 20_000);
205
- });
206
-
207
- describe('revalidateTag expire settings (next start)', () => {
208
- // Expire seconds from next/dist/server/config-shared.js cacheLife presets
209
- // (identical in Next.js 16.0.11, 16.2.6, 16.3.0). `default.expire` is
210
- // INFINITE_CACHE = 0xfffffffe.
211
- const CACHE_LIFE_EXPIRE_SECONDS = {
212
- default: 0xfffffffe,
213
- seconds: 60,
214
- minutes: 60 * 60,
215
- hours: 60 * 60 * 24,
216
- days: 60 * 60 * 24 * 7,
217
- weeks: 60 * 60 * 24 * 30,
218
- max: 60 * 60 * 24 * 365,
219
- } as const;
220
-
221
- async function getMatrix(id: string) {
222
- const res = await fetch(
223
- `${BASE_URL}/api/expire-matrix?id=${encodeURIComponent(id)}`,
224
- );
225
- expect(res.status).toBe(200);
226
- return res.json() as Promise<{
227
- counter: number;
228
- timestamp: number;
229
- id: string;
230
- }>;
231
- }
232
-
233
- async function revalidateMatrix(
234
- id: string,
235
- profile: string | { expire: number } | null,
236
- ) {
237
- const tag = `expire-matrix-${id}`;
238
- const body = profile === null ? { tag, profile: null } : { tag, profile };
239
- const res = await fetch(`${BASE_URL}/api/expire-matrix/revalidate`, {
240
- method: 'POST',
241
- headers: { 'Content-Type': 'application/json' },
242
- body: JSON.stringify(body),
243
- });
244
- expect(res.status).toBe(200);
245
- return res.json() as Promise<{ timestamp: number }>;
246
- }
247
-
248
- async function readTagManifest(tag: string) {
249
- const hashKey = `${keyPrefix}__cacheComponents_revalidated_tags__`;
250
- for (let elapsed = 0; elapsed < 5_000; elapsed += 50) {
251
- const raw = await redisClient.hGet(hashKey, tag);
252
- if (raw) {
253
- const parsed: unknown = JSON.parse(raw);
254
- if (typeof parsed === 'number') {
255
- return { expired: parsed } as { stale?: number; expired?: number };
256
- }
257
- return parsed as { stale?: number; expired?: number };
258
- }
259
- await delay(50);
260
- }
261
- throw new Error(`tag manifest missing for ${tag} in ${hashKey}`);
262
- }
263
-
264
- function expectApproxMs(actual: number | undefined, expected: number) {
265
- expect(actual, `expected ~${expected}, got ${actual}`).toBeDefined();
266
- expect(Math.abs(actual! - expected)).toBeLessThan(5_000);
267
- }
268
-
269
- it.each(Object.entries(CACHE_LIFE_EXPIRE_SECONDS) as [string, number][])(
270
- 'revalidateTag(tag, %s) marks stale now and expired=now+expire*1000, then SWR',
271
- async (profile, expireSeconds) => {
272
- const id = `profile-${profile}`;
273
- await getMatrix(id);
274
- const before = await getMatrix(id);
275
-
276
- const startedAt = Date.now();
277
- await revalidateMatrix(id, profile);
278
- const manifest = await readTagManifest(`expire-matrix-${id}`);
279
-
280
- expectApproxMs(manifest.stale, startedAt);
281
- expectApproxMs(manifest.expired, startedAt + expireSeconds * 1000);
282
-
283
- const after = await getMatrix(id);
284
- expect(after.counter).toBe(before.counter);
285
- expect(after.timestamp).toBe(before.timestamp);
286
- },
287
- );
288
-
289
- it('{ expire: 5 } uses the object expire, not a named profile', async () => {
290
- const id = 'object-expire-5';
291
- await getMatrix(id);
292
- const before = await getMatrix(id);
293
-
294
- const startedAt = Date.now();
295
- await revalidateMatrix(id, { expire: 5 });
296
- const manifest = await readTagManifest(`expire-matrix-${id}`);
297
-
298
- expectApproxMs(manifest.stale, startedAt);
299
- expectApproxMs(manifest.expired, startedAt + 5_000);
300
-
301
- const after = await getMatrix(id);
302
- expect(after.counter).toBe(before.counter);
303
- });
304
-
305
- it('{ expire: 0 } writes expired=now and the next GET is a blocking miss', async () => {
306
- const id = 'expire-zero';
307
- await getMatrix(id);
308
- const before = await getMatrix(id);
309
-
310
- const startedAt = Date.now();
311
- await revalidateMatrix(id, { expire: 0 });
312
- const manifest = await readTagManifest(`expire-matrix-${id}`);
313
-
314
- expectApproxMs(manifest.expired, startedAt);
315
- expect(manifest.stale).toBeUndefined();
316
-
317
- const after = await getMatrix(id);
318
- expect(after.counter).toBeGreaterThan(before.counter);
319
- });
320
-
321
- it('omitted profile writes expired=now without stale and the next GET misses', async () => {
322
- const id = 'omit-profile';
323
- await getMatrix(id);
324
- const before = await getMatrix(id);
325
-
326
- const startedAt = Date.now();
327
- await revalidateMatrix(id, null);
328
- const manifest = await readTagManifest(`expire-matrix-${id}`);
329
-
330
- expect(manifest.stale).toBeUndefined();
331
- expectApproxMs(manifest.expired, startedAt);
332
-
333
- const after = await getMatrix(id);
334
- expect(after.counter).toBeGreaterThan(before.counter);
335
- });
336
-
337
- it('{ expire: 2 } serves stale, then hard-expires after the window', async () => {
338
- const id = 'expire-two';
339
- await getMatrix(id);
340
- const before = await getMatrix(id);
341
-
342
- const startedAt = Date.now();
343
- await revalidateMatrix(id, { expire: 2 });
344
- const manifest = await readTagManifest(`expire-matrix-${id}`);
345
-
346
- expectApproxMs(manifest.stale, startedAt);
347
- expectApproxMs(manifest.expired, startedAt + 2_000);
348
-
349
- const stale = await getMatrix(id);
350
- expect(stale.counter).toBe(before.counter);
351
-
352
- await delay(3_000);
353
-
354
- const afterWindow = await getMatrix(id);
355
- expect(afterWindow.counter).toBeGreaterThan(before.counter);
356
- }, 15_000);
357
- });
358
-
359
- describe('Redis cache handler integration', () => {
360
- it('should call cache handler get and set methods', async () => {
361
- // Make request to trigger cache (don't clear first)
362
- await fetch(`${BASE_URL}/api/cached-static-fetch`);
363
-
364
- // Verify Redis has the cached data
365
- const redisKeys = await redisClient.keys(`${keyPrefix}*`);
366
- expect(redisKeys.length).toBeGreaterThan(0);
367
-
368
- // Filter out hash keys (sharedTagsMap) and only check string keys (cache entries)
369
- // Try to get each key and verify at least one is a string value
370
- let foundStringKey = false;
371
- for (const key of redisKeys) {
372
- try {
373
- const type = await redisClient.type(key);
374
- if (type === 'string') {
375
- const cachedValue = await redisClient.get(key);
376
- if (cachedValue) {
377
- foundStringKey = true;
378
- expect(cachedValue).toBeTruthy();
379
- break;
380
- }
381
- }
382
- } catch (e) {
383
- // Skip non-string keys
384
- }
385
- }
386
- expect(foundStringKey).toBe(true);
387
- });
388
- });
389
- });
@@ -1,65 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { spawn } from 'child_process';
3
- import path from 'path';
4
- import { spawnSync } from 'child_process';
5
-
6
- function hasContainerRuntime() {
7
- const podman = spawnSync('podman', ['--version'], {
8
- encoding: 'utf8',
9
- timeout: 3_000,
10
- });
11
- if (!podman.error && (podman.status ?? 1) === 0) return true;
12
-
13
- const docker = spawnSync('docker', ['--version'], {
14
- encoding: 'utf8',
15
- timeout: 3_000,
16
- });
17
- return !docker.error && (docker.status ?? 1) === 0;
18
- }
19
-
20
- function runNode(script: string, timeoutMs = 120_000) {
21
- return new Promise<{ code: number; stdout: string; stderr: string }>(
22
- (resolve, reject) => {
23
- const p = spawn('pnpm', ['-s', 'tsx', script], {
24
- stdio: ['ignore', 'pipe', 'pipe'],
25
- env: { ...process.env },
26
- });
27
- let stdout = '';
28
- let stderr = '';
29
- p.stdout.on('data', (d) => (stdout += d.toString()));
30
- p.stderr.on('data', (d) => (stderr += d.toString()));
31
- const t = setTimeout(() => {
32
- p.kill('SIGKILL');
33
- reject(new Error('timeout'));
34
- }, timeoutMs);
35
- p.on('close', (code) => {
36
- clearTimeout(t);
37
- resolve({ code: code ?? -1, stdout, stderr });
38
- });
39
- },
40
- );
41
- }
42
-
43
- describe('redis kill/reconnect end-to-end (both handlers)', () => {
44
- const runOrSkip = hasContainerRuntime() ? it : it.skip;
45
-
46
- runOrSkip(
47
- 'survives redis restart without Socket already opened',
48
- async () => {
49
- const script = path.join(__dirname, 'scripts', 'redis-kill-reconnect.ts');
50
-
51
- const res = await runNode(script, 180_000);
52
-
53
- if (res.code !== 0) {
54
- console.error('Script exited with code', res.code);
55
- console.error('stdout:', res.stdout);
56
- console.error('stderr:', res.stderr);
57
- }
58
-
59
- expect(res.code).toBe(0);
60
- expect(res.stdout).toContain('OK');
61
- expect(res.stderr).not.toContain('Socket already opened');
62
- },
63
- 180_000,
64
- );
65
- });
@@ -1,59 +0,0 @@
1
- import { describe, it, expect, beforeAll } from 'vitest';
2
- import path from 'path';
3
- import {
4
- hasContainerRuntime,
5
- runNode,
6
- parseResults,
7
- type TestResult,
8
- } from './scripts/redis-test-helpers';
9
-
10
- const describeOrSkip = hasContainerRuntime() ? describe : describe.skip;
11
-
12
- describeOrSkip('subscriber teardown: quit() vs disconnect()', () => {
13
- let results: Map<string, TestResult>;
14
- let exitCode: number;
15
- let stderr: string;
16
-
17
- beforeAll(async () => {
18
- const script = path.join(
19
- __dirname,
20
- 'scripts',
21
- 'redis-quit-vs-disconnect.ts',
22
- );
23
- const res = await runNode(script, 60_000);
24
- exitCode = res.code;
25
- stderr = res.stderr;
26
- results = parseResults(res.stdout);
27
- }, 60_000);
28
-
29
- it('script exits successfully', () => {
30
- if (exitCode !== 0) {
31
- process.stderr.write(stderr);
32
- }
33
- expect(exitCode).toBe(0);
34
- });
35
-
36
- it('subscriber clients are ready before outage', () => {
37
- const r = results.get('subscribers-ready');
38
- expect(r).toBeDefined();
39
- expect(r!.pass, r!.detail).toBe(true);
40
- });
41
-
42
- it('quit() does not immediately close a subscriber during outage', () => {
43
- const r = results.get('quit-does-not-immediately-close');
44
- expect(r).toBeDefined();
45
- expect(r!.pass, r!.detail).toBe(true);
46
- });
47
-
48
- it('disconnect() closes the subscriber immediately during outage', () => {
49
- const r = results.get('disconnect-closes-immediately');
50
- expect(r).toBeDefined();
51
- expect(r!.pass, r!.detail).toBe(true);
52
- });
53
-
54
- it('disconnected subscriber stays dead after Redis restarts', () => {
55
- const r = results.get('disconnected-client-stays-dead');
56
- expect(r).toBeDefined();
57
- expect(r!.pass, r!.detail).toBe(true);
58
- });
59
- });
@@ -1,100 +0,0 @@
1
- import { describe, it, expect, beforeAll } from 'vitest';
2
- import path from 'path';
3
- import {
4
- hasContainerRuntime,
5
- runNode,
6
- parseResults,
7
- type TestResult,
8
- } from './scripts/redis-test-helpers';
9
-
10
- const describeOrSkip = hasContainerRuntime() ? describe : describe.skip;
11
-
12
- describeOrSkip('issue #86: subscriber outage recovery', () => {
13
- let results: Map<string, TestResult>;
14
-
15
- beforeAll(async () => {
16
- const script = path.join(
17
- __dirname,
18
- 'scripts',
19
- 'redis-subscriber-outage.ts',
20
- );
21
- const res = await runNode(script, 300_000);
22
- // Script always exits 0 (it records pass/fail per sub-test)
23
- if (res.code !== 0) {
24
- throw new Error(
25
- `Script exited with code ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`,
26
- );
27
- }
28
- results = parseResults(res.stdout);
29
- }, 300_000);
30
-
31
- // --- Tests that should PASS (verify correct behavior before outage) ---
32
-
33
- it('PubSub sync works before outage', () => {
34
- const r = results.get('pubsub-initial-strings');
35
- expect(r).toBeDefined();
36
- expect(r!.pass, r!.detail).toBe(true);
37
- });
38
-
39
- it('subscriber clients have error listeners before outage', () => {
40
- const r = results.get('error-listener-initial');
41
- expect(r).toBeDefined();
42
- expect(r!.pass, r!.detail).toBe(true);
43
- });
44
-
45
- // --- Tests that verify symptoms during outage ---
46
-
47
- it('get() returns null during outage', () => {
48
- const r = results.get('get-during-outage');
49
- expect(r).toBeDefined();
50
- expect(r!.pass, r!.detail).toBe(true);
51
- });
52
-
53
- it('heap does not grow excessively during 50 get() calls in outage', () => {
54
- const r = results.get('heap-growth-during-outage');
55
- expect(r).toBeDefined();
56
- expect(r!.pass, r!.detail).toBe(true);
57
- });
58
-
59
- it('main client reconnects after Redis restart', () => {
60
- const r = results.get('main-client-reconnects');
61
- expect(r).toBeDefined();
62
- expect(r!.pass, r!.detail).toBe(true);
63
- });
64
-
65
- it('get() works after Redis restart (main client path)', () => {
66
- const r = results.get('get-after-outage');
67
- expect(r).toBeDefined();
68
- expect(r!.pass, r!.detail).toBe(true);
69
- });
70
-
71
- // --- Tests that reproduce the bug (EXPECTED TO FAIL with current code) ---
72
-
73
- it('PubSub sync works after outage (issue #86 — subscriber never recovers)', () => {
74
- const r = results.get('pubsub-after-outage-strings');
75
- expect(r).toBeDefined();
76
- expect(r!.pass, r!.detail).toBe(true);
77
- });
78
-
79
- it('subscriber error listener state after outage (informational)', () => {
80
- const r = results.get('error-listener-after-outage');
81
- expect(r).toBeDefined();
82
- // Informational — always passes. Detail shows whether the listener
83
- // survived and whether the subscriber client object was replaced.
84
- expect(r!.pass, r!.detail).toBe(true);
85
- });
86
-
87
- it('duplicate() call count during outage (informational)', () => {
88
- const r = results.get('duplicate-count-during-outage');
89
- expect(r).toBeDefined();
90
- // Informational — always passes. Detail has the actual count.
91
- expect(r!.pass, r!.detail).toBe(true);
92
- });
93
-
94
- it('total duplicate() call count (informational)', () => {
95
- const r = results.get('duplicate-count-total');
96
- expect(r).toBeDefined();
97
- // Informational — always passes. Detail has the actual count.
98
- expect(r!.pass, r!.detail).toBe(true);
99
- });
100
- });