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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +87 -24
  2. package/dist/index.d.mts +40 -1
  3. package/dist/index.d.ts +40 -1
  4. package/dist/index.js +291 -116
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +288 -115
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +9 -6
  9. package/.cursor/skills/testing/SKILL.md +0 -121
  10. package/.github/workflows/ci.yml +0 -309
  11. package/.github/workflows/pages.yml +0 -38
  12. package/.github/workflows/release.yml +0 -207
  13. package/.prettierrc.json +0 -6
  14. package/.vscode/settings.json +0 -10
  15. package/ARCHITECTURE.md +0 -431
  16. package/CHANGELOG.md +0 -411
  17. package/commitlint.config.cjs +0 -4
  18. package/docker/redis.conf +0 -1
  19. package/docs/index.html +0 -140
  20. package/docs/robots.txt +0 -4
  21. package/docs/sitemap.xml +0 -8
  22. package/docs/styles.css +0 -141
  23. package/eslint.config.mjs +0 -26
  24. package/hooks/commit-msg +0 -10
  25. package/hooks/pre-commit +0 -11
  26. package/playwright.config.ts +0 -18
  27. package/release.config.cjs +0 -31
  28. package/scripts/prepare.sh +0 -7
  29. package/scripts/setup-git.sh +0 -8
  30. package/scripts/setup-repo-name.sh +0 -17
  31. package/scripts/vitest-run-staged.cjs +0 -44
  32. package/src/CacheComponentsHandler.ts +0 -567
  33. package/src/CachedHandler.ts +0 -53
  34. package/src/DeduplicatedRequestHandler.ts +0 -110
  35. package/src/RedisStringsHandler.ts +0 -937
  36. package/src/SyncedMap.ts +0 -482
  37. package/src/ZodHandler.ts +0 -45
  38. package/src/index.ts +0 -16
  39. package/src/serializer.ts +0 -59
  40. package/src/utils/debug.ts +0 -30
  41. package/src/utils/json.ts +0 -49
  42. package/src/utils/prefix.ts +0 -44
  43. package/test/README.md +0 -194
  44. package/test/nextjs-test-projects/next-app-15-4-11/README.md +0 -36
  45. package/test/nextjs-test-projects/next-app-15-4-11/eslint.config.mjs +0 -16
  46. package/test/nextjs-test-projects/next-app-15-4-11/next.config.js +0 -9
  47. package/test/nextjs-test-projects/next-app-15-4-11/package-lock.json +0 -5969
  48. package/test/nextjs-test-projects/next-app-15-4-11/package.json +0 -29
  49. package/test/nextjs-test-projects/next-app-15-4-11/pnpm-lock.yaml +0 -3726
  50. package/test/nextjs-test-projects/next-app-15-4-11/postcss.config.mjs +0 -5
  51. package/test/nextjs-test-projects/next-app-15-4-11/public/file.svg +0 -1
  52. package/test/nextjs-test-projects/next-app-15-4-11/public/globe.svg +0 -1
  53. package/test/nextjs-test-projects/next-app-15-4-11/public/next.svg +0 -1
  54. package/test/nextjs-test-projects/next-app-15-4-11/public/vercel.svg +0 -1
  55. package/test/nextjs-test-projects/next-app-15-4-11/public/window.svg +0 -1
  56. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/cached-static-fetch/route.ts +0 -18
  57. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  58. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidatePath/route.ts +0 -15
  59. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidateTag/route.ts +0 -15
  60. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidated-fetch/route.ts +0 -17
  61. package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/uncached-fetch/route.ts +0 -15
  62. package/test/nextjs-test-projects/next-app-15-4-11/src/app/globals.css +0 -26
  63. package/test/nextjs-test-projects/next-app-15-4-11/src/app/layout.tsx +0 -59
  64. package/test/nextjs-test-projects/next-app-15-4-11/src/app/page.tsx +0 -755
  65. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  66. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  67. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  68. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  69. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  70. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  71. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  72. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  73. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  74. package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  75. package/test/nextjs-test-projects/next-app-15-4-11/src/app/revalidation-interface.tsx +0 -267
  76. package/test/nextjs-test-projects/next-app-15-4-11/tsconfig.json +0 -27
  77. package/test/nextjs-test-projects/next-app-16-0-11/README.md +0 -36
  78. package/test/nextjs-test-projects/next-app-16-0-11/eslint.config.mjs +0 -18
  79. package/test/nextjs-test-projects/next-app-16-0-11/next.config.ts +0 -10
  80. package/test/nextjs-test-projects/next-app-16-0-11/package.json +0 -28
  81. package/test/nextjs-test-projects/next-app-16-0-11/pnpm-lock.yaml +0 -4163
  82. package/test/nextjs-test-projects/next-app-16-0-11/postcss.config.mjs +0 -7
  83. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/cached-static-fetch/route.ts +0 -18
  84. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  85. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidatePath/route.ts +0 -15
  86. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidateTag/route.ts +0 -20
  87. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidated-fetch/route.ts +0 -17
  88. package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/uncached-fetch/route.ts +0 -15
  89. package/test/nextjs-test-projects/next-app-16-0-11/src/app/favicon.ico +0 -0
  90. package/test/nextjs-test-projects/next-app-16-0-11/src/app/globals.css +0 -26
  91. package/test/nextjs-test-projects/next-app-16-0-11/src/app/layout.tsx +0 -59
  92. package/test/nextjs-test-projects/next-app-16-0-11/src/app/page.tsx +0 -755
  93. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  94. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  95. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  96. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  97. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  98. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  99. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  100. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  101. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  102. package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  103. package/test/nextjs-test-projects/next-app-16-0-11/src/app/revalidation-interface.tsx +0 -267
  104. package/test/nextjs-test-projects/next-app-16-0-11/src/app/update-tag-test/page.tsx +0 -25
  105. package/test/nextjs-test-projects/next-app-16-0-11/tsconfig.json +0 -34
  106. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/README.md +0 -36
  107. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/cache-handler.js +0 -3
  108. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/eslint.config.mjs +0 -18
  109. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/next.config.ts +0 -13
  110. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/package.json +0 -28
  111. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/pnpm-lock.yaml +0 -4206
  112. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/postcss.config.mjs +0 -7
  113. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/file.svg +0 -1
  114. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/globe.svg +0 -1
  115. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/next.svg +0 -1
  116. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/vercel.svg +0 -1
  117. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/window.svg +0 -1
  118. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  119. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  120. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  121. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  122. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  123. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  124. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/page.tsx +0 -90
  125. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  126. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  127. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  128. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  129. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/favicon.ico +0 -0
  130. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/globals.css +0 -26
  131. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/layout.tsx +0 -57
  132. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/page.tsx +0 -755
  133. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/revalidation-interface.tsx +0 -267
  134. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/update-tag-test/page.tsx +0 -22
  135. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/tsconfig.json +0 -34
  136. package/test/nextjs-test-projects/next-app-16-2-6/README.md +0 -36
  137. package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +0 -18
  138. package/test/nextjs-test-projects/next-app-16-2-6/next.config.ts +0 -7
  139. package/test/nextjs-test-projects/next-app-16-2-6/package.json +0 -28
  140. package/test/nextjs-test-projects/next-app-16-2-6/pnpm-lock.yaml +0 -4165
  141. package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +0 -7
  142. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/cached-static-fetch/route.ts +0 -18
  143. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  144. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidatePath/route.ts +0 -15
  145. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidateTag/route.ts +0 -20
  146. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidated-fetch/route.ts +0 -17
  147. package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/uncached-fetch/route.ts +0 -15
  148. package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
  149. package/test/nextjs-test-projects/next-app-16-2-6/src/app/globals.css +0 -26
  150. package/test/nextjs-test-projects/next-app-16-2-6/src/app/layout.tsx +0 -59
  151. package/test/nextjs-test-projects/next-app-16-2-6/src/app/page.tsx +0 -755
  152. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  153. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  154. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  155. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  156. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  157. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  158. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  159. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  160. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  161. package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  162. package/test/nextjs-test-projects/next-app-16-2-6/src/app/revalidation-interface.tsx +0 -267
  163. package/test/nextjs-test-projects/next-app-16-2-6/src/app/update-tag-test/page.tsx +0 -25
  164. package/test/nextjs-test-projects/next-app-16-2-6/tsconfig.json +0 -34
  165. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/README.md +0 -36
  166. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +0 -3
  167. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/eslint.config.mjs +0 -18
  168. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +0 -13
  169. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/package.json +0 -28
  170. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/pnpm-lock.yaml +0 -4165
  171. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/postcss.config.mjs +0 -7
  172. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/file.svg +0 -1
  173. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/globe.svg +0 -1
  174. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/next.svg +0 -1
  175. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/vercel.svg +0 -1
  176. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/window.svg +0 -1
  177. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  178. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  179. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  180. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  181. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  182. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  183. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +0 -90
  184. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  185. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  186. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  187. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  188. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/favicon.ico +0 -0
  189. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/globals.css +0 -26
  190. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/layout.tsx +0 -57
  191. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/page.tsx +0 -755
  192. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/revalidation-interface.tsx +0 -267
  193. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +0 -22
  194. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/tsconfig.json +0 -34
  195. package/test/nextjs-test-projects/next-app-16-3-0/README.md +0 -36
  196. package/test/nextjs-test-projects/next-app-16-3-0/eslint.config.mjs +0 -18
  197. package/test/nextjs-test-projects/next-app-16-3-0/next.config.ts +0 -7
  198. package/test/nextjs-test-projects/next-app-16-3-0/package.json +0 -28
  199. package/test/nextjs-test-projects/next-app-16-3-0/pnpm-lock.yaml +0 -4284
  200. package/test/nextjs-test-projects/next-app-16-3-0/postcss.config.mjs +0 -7
  201. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/cached-static-fetch/route.ts +0 -18
  202. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  203. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidatePath/route.ts +0 -15
  204. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidateTag/route.ts +0 -20
  205. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidated-fetch/route.ts +0 -17
  206. package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/uncached-fetch/route.ts +0 -15
  207. package/test/nextjs-test-projects/next-app-16-3-0/src/app/favicon.ico +0 -0
  208. package/test/nextjs-test-projects/next-app-16-3-0/src/app/globals.css +0 -26
  209. package/test/nextjs-test-projects/next-app-16-3-0/src/app/layout.tsx +0 -59
  210. package/test/nextjs-test-projects/next-app-16-3-0/src/app/page.tsx +0 -755
  211. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  212. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  213. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  214. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  215. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  216. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  217. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  218. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  219. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  220. package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  221. package/test/nextjs-test-projects/next-app-16-3-0/src/app/revalidation-interface.tsx +0 -267
  222. package/test/nextjs-test-projects/next-app-16-3-0/src/app/update-tag-test/page.tsx +0 -25
  223. package/test/nextjs-test-projects/next-app-16-3-0/tsconfig.json +0 -34
  224. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/README.md +0 -36
  225. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/cache-handler.js +0 -3
  226. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/eslint.config.mjs +0 -18
  227. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/next.config.ts +0 -13
  228. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/package.json +0 -28
  229. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/pnpm-lock.yaml +0 -4284
  230. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/postcss.config.mjs +0 -7
  231. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/file.svg +0 -1
  232. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/globe.svg +0 -1
  233. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/next.svg +0 -1
  234. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/vercel.svg +0 -1
  235. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/window.svg +0 -1
  236. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
  237. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
  238. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
  239. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
  240. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
  241. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
  242. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/page.tsx +0 -90
  243. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
  244. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
  245. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
  246. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
  247. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/favicon.ico +0 -0
  248. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/globals.css +0 -26
  249. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/layout.tsx +0 -57
  250. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/page.tsx +0 -755
  251. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/revalidation-interface.tsx +0 -267
  252. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/update-tag-test/page.tsx +0 -22
  253. package/test/nextjs-test-projects/next-app-16-3-0-cache-components/tsconfig.json +0 -34
  254. package/test/nextjs-test-projects/next-app-customized/README.md +0 -36
  255. package/test/nextjs-test-projects/next-app-customized/customized-cache-handler.js +0 -34
  256. package/test/nextjs-test-projects/next-app-customized/eslint.config.mjs +0 -16
  257. package/test/nextjs-test-projects/next-app-customized/next.config.js +0 -6
  258. package/test/nextjs-test-projects/next-pages-16-2-6/README.md +0 -16
  259. package/test/nextjs-test-projects/next-pages-16-2-6/eslint.config.mjs +0 -18
  260. package/test/nextjs-test-projects/next-pages-16-2-6/next.config.ts +0 -7
  261. package/test/nextjs-test-projects/next-pages-16-2-6/package.json +0 -26
  262. package/test/nextjs-test-projects/next-pages-16-2-6/pnpm-lock.yaml +0 -3896
  263. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/api/revalidate.ts +0 -24
  264. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/index.tsx +0 -11
  265. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/isr/[slug].tsx +0 -49
  266. package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/static-forever.tsx +0 -20
  267. package/test/nextjs-test-projects/next-pages-16-2-6/tsconfig.json +0 -34
  268. package/test/nextjs-test-projects/next-pages-16-3-0/README.md +0 -16
  269. package/test/nextjs-test-projects/next-pages-16-3-0/eslint.config.mjs +0 -18
  270. package/test/nextjs-test-projects/next-pages-16-3-0/next.config.ts +0 -7
  271. package/test/nextjs-test-projects/next-pages-16-3-0/package.json +0 -26
  272. package/test/nextjs-test-projects/next-pages-16-3-0/pnpm-lock.yaml +0 -3939
  273. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/api/revalidate.ts +0 -24
  274. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/index.tsx +0 -11
  275. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/isr/[slug].tsx +0 -49
  276. package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/static-forever.tsx +0 -20
  277. package/test/nextjs-test-projects/next-pages-16-3-0/tsconfig.json +0 -34
  278. package/test/playwright/cache-lab.spec.ts +0 -157
  279. package/test/playwright/update-tag.spec.ts +0 -33
  280. package/test/vitest/integration/build-id-prefix.integration.test.ts +0 -102
  281. package/test/vitest/integration/cache-components/cache-components.integration.test.ts +0 -231
  282. package/test/vitest/integration/cache-components/redis-kill-reconnect.test.ts +0 -65
  283. package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +0 -59
  284. package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +0 -100
  285. package/test/vitest/integration/cache-components/scripts/redis-kill-reconnect.ts +0 -243
  286. package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +0 -113
  287. package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +0 -379
  288. package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +0 -155
  289. package/test/vitest/integration/nextjs-cache-handler.integration.test.ts +0 -945
  290. package/test/vitest/integration/pages-router.integration.test.ts +0 -420
  291. package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +0 -154
  292. package/test/vitest/unit/index.test.ts +0 -173
  293. package/test/vitest/unit/pages-router-kinds.test.ts +0 -292
  294. package/test/vitest/unit/reconnect-socket-already-opened.test.ts +0 -117
  295. package/test/vitest/unit/serializer.test.ts +0 -182
  296. package/test/vitest/unit/utils/prefix.test.ts +0 -115
  297. package/tsconfig.json +0 -18
  298. package/tsup.config.ts +0 -14
  299. package/vite.config.ts +0 -34
  300. package/vitest.cache-components.config.ts +0 -16
@@ -1,945 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
- import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
3
- import fetch from 'node-fetch';
4
- import { createClient, RedisClientType } from 'redis';
5
- import { join } from 'path';
6
- import { CacheEntry } from '../../../src/RedisStringsHandler';
7
- import { revalidate as revalidatedFetchRouteRevalidate } from '../../nextjs-test-projects/next-app-15-4-11/src/app/api/revalidated-fetch/route';
8
-
9
- // Select which Next.js test app to use. Can be overridden via NEXT_TEST_APP env var
10
- // Examples: next-app-15-4-11, next-app-16-0-11, next-app-16-2-6, next-app-16-3-0
11
- const NEXT_TEST_APP = process.env.NEXT_TEST_APP || 'next-app-15-4-11';
12
- const NEXT_APP_DIR = join(
13
- __dirname,
14
- '..',
15
- '..',
16
- 'nextjs-test-projects',
17
- NEXT_TEST_APP,
18
- );
19
- console.log('NEXT_APP_DIR', NEXT_APP_DIR);
20
-
21
- const SHARED_TAGS_KEY = '__sharedTags__';
22
- const NEXT_START_PORT = 3055;
23
- const NEXT_START_URL = `http://localhost:${NEXT_START_PORT}`;
24
-
25
- const REDIS_BACKGROUND_SYNC_DELAY = 250; //ms delay to prevent flaky tests in slow CI environments
26
-
27
- let nextProcess: ChildProcessWithoutNullStreams;
28
- let redisClient: RedisClientType;
29
-
30
- async function delay(ms: number) {
31
- return new Promise((resolve) => setTimeout(resolve, ms));
32
- }
33
-
34
- async function findFetchCacheEntryKey(tag: string): Promise<string> {
35
- const sharedTags = await redisClient.hGetAll(
36
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
37
- );
38
- const matches = Object.entries(sharedTags).filter(
39
- ([key, serializedTags]) =>
40
- !key.startsWith('/') &&
41
- (JSON.parse(serializedTags) as string[]).includes(tag),
42
- );
43
-
44
- expect(matches).toHaveLength(1);
45
- return matches[0]![0];
46
- }
47
-
48
- async function runCommand(cmd: string, args: string[], cwd: string) {
49
- return new Promise((resolve, reject) => {
50
- let stderr = '';
51
- let stdout = '';
52
- const proc = spawn(cmd, args, { cwd, stdio: 'pipe' });
53
-
54
- proc.stdout.on('data', (data) => {
55
- if (process.env.DEBUG_INTEGRATION) {
56
- console.log(data.toString());
57
- }
58
- stdout += data.toString();
59
- });
60
-
61
- proc.stderr.on('data', (data) => {
62
- if (process.env.DEBUG_INTEGRATION) {
63
- console.error(data.toString());
64
- }
65
- stderr += data.toString();
66
- });
67
-
68
- proc.on('exit', (code) => {
69
- if (code === 0) resolve(undefined);
70
- else {
71
- reject(
72
- new Error(
73
- `${cmd} ${args.join(' ')} failed with code ${code}\n` +
74
- `stdout: ${stdout}\n` +
75
- `stderr: ${stderr}`,
76
- ),
77
- );
78
- }
79
- });
80
- });
81
- }
82
-
83
- async function waitForServer(url: string, timeout = 20000) {
84
- const start = Date.now();
85
- while (Date.now() - start < timeout) {
86
- try {
87
- const res = await fetch(url + '/api/cached-static-fetch');
88
- if (res.ok) return;
89
- } catch {}
90
- await new Promise((r) => setTimeout(r, 300));
91
- }
92
- throw new Error('Next.js server did not start in time');
93
- }
94
-
95
- describe('Next.js Turbo Redis Cache Integration', () => {
96
- beforeAll(async () => {
97
- // If there was detected to run a server before (any old server which was not stopped correctly), kill it
98
- try {
99
- const res = await fetch(NEXT_START_URL + '/api/cached-static-fetch');
100
- if (res.ok) {
101
- await runCommand('pkill', ['next'], NEXT_APP_DIR);
102
- }
103
- } catch {}
104
-
105
- // Set up environment variables
106
- process.env.VERCEL_ENV = 'production';
107
- process.env.VERCEL_URL =
108
- 'integration-test-' + Math.random().toString(36).substring(2, 15);
109
- console.log('redis key prefix is:', process.env.VERCEL_URL);
110
-
111
- // Only override if redis env vars if not set. This can be set in the CI env.
112
- process.env.REDISHOST = process.env.REDISHOST || 'localhost';
113
- process.env.REDISPORT = process.env.REDISPORT || '6379';
114
- process.env.NEXT_START_PORT = String(NEXT_START_PORT);
115
-
116
- if (process.env.SKIP_BUILD === 'true') {
117
- console.log('skipping build');
118
- } else {
119
- // Build Next.js app first
120
- await runCommand('pnpm', ['i'], NEXT_APP_DIR);
121
- console.log('pnpm i done');
122
- await runCommand('pnpm', ['build'], NEXT_APP_DIR);
123
- console.log('pnpm build done');
124
- }
125
-
126
- // Start Next.js app
127
- nextProcess = spawn(
128
- 'npx',
129
- ['next', 'start', '-p', String(NEXT_START_PORT)],
130
- {
131
- cwd: NEXT_APP_DIR,
132
- env: {
133
- ...process.env,
134
- },
135
- stdio: 'pipe',
136
- },
137
- );
138
- if (process.env.DEBUG_INTEGRATION) {
139
- nextProcess.stdout.on('data', (data) => {
140
- console.log(`stdout: ${data}`);
141
- });
142
- }
143
-
144
- nextProcess.stderr.on('data', (data) => {
145
- console.error(`stderr: ${data}`);
146
- });
147
- await waitForServer(NEXT_START_URL);
148
- console.log('next start successful');
149
-
150
- // Connect to Redis
151
- redisClient = createClient({
152
- url: `redis://${process.env.REDISHOST}:${process.env.REDISPORT}`,
153
- });
154
- await redisClient.connect();
155
-
156
- console.log('redis key prefix is:', process.env.VERCEL_URL);
157
- }, 60_000);
158
-
159
- afterAll(async () => {
160
- if (process.env.KEEP_SERVER_RUNNING === 'true') {
161
- console.log('keeping server running');
162
- } else {
163
- if (nextProcess) nextProcess.kill();
164
- }
165
- if (redisClient) await redisClient.quit();
166
- });
167
-
168
- describe('should have the correct caching behavior for API routes', () => {
169
- describe('should cache static API routes in Redis', () => {
170
- let counter1: number;
171
-
172
- it('First request (should increment counter)', async () => {
173
- const res = await fetch(NEXT_START_URL + '/api/cached-static-fetch');
174
- const data: any = await res.json();
175
- expect(data.counter).toBe(1);
176
- counter1 = data.counter;
177
- });
178
-
179
- it('Second request (should hit cache, counter should not increment if cache works)', async () => {
180
- const res = await fetch(NEXT_START_URL + '/api/cached-static-fetch');
181
- const data: any = await res.json();
182
- // If cache is working, counter should stay 1; if not, it will increment
183
- expect(data.counter).toBe(counter1);
184
- });
185
-
186
- it('The data in the redis key should match the expected format', async () => {
187
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
188
- const keys = await redisClient.keys(process.env.VERCEL_URL + '*');
189
- expect(keys.length).toBeGreaterThan(0);
190
-
191
- // check the content of redis key
192
- const value = (await redisClient.get(
193
- process.env.VERCEL_URL + '/api/cached-static-fetch',
194
- )) as string;
195
- expect(value).toBeDefined();
196
- const cacheEntry: CacheEntry = JSON.parse(value);
197
-
198
- // The format should be as expected
199
- expect(cacheEntry).toEqual({
200
- value: {
201
- kind: 'APP_ROUTE',
202
- status: 200,
203
- body: { $binary: 'eyJjb3VudGVyIjoxfQ==' },
204
- headers: {
205
- 'cache-control': 'public, max-age=1',
206
- 'content-type': 'application/json',
207
- 'x-next-cache-tags':
208
- '_N_T_/layout,_N_T_/api/layout,_N_T_/api/cached-static-fetch/layout,_N_T_/api/cached-static-fetch/route,_N_T_/api/cached-static-fetch',
209
- },
210
- },
211
- lastModified: expect.any(Number),
212
- tags: [
213
- '_N_T_/layout',
214
- '_N_T_/api/layout',
215
- '_N_T_/api/cached-static-fetch/layout',
216
- '_N_T_/api/cached-static-fetch/route',
217
- '_N_T_/api/cached-static-fetch',
218
- ],
219
- });
220
-
221
- expect((cacheEntry.value as any).kind).toBe('APP_ROUTE');
222
- const bodyBuffer = Buffer.from(
223
- (cacheEntry.value as any)?.body?.$binary,
224
- 'base64',
225
- );
226
- const bodyJson = JSON.parse(bodyBuffer.toString('utf-8'));
227
- expect(bodyJson.counter).toBe(counter1);
228
- });
229
-
230
- it('A request to revalidatePath API should remove the route from redis (string and hashmap)', async () => {
231
- const revalidateRes = await fetch(
232
- NEXT_START_URL + '/api/revalidatePath?path=/api/cached-static-fetch',
233
- );
234
- const revalidateResJson: any = await revalidateRes.json();
235
- expect(revalidateResJson.success).toBe(true);
236
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
237
-
238
- // check Redis keys
239
- const keys = await redisClient.keys(
240
- process.env.VERCEL_URL + '/api/cached-static-fetch',
241
- );
242
- expect(keys.length).toBe(0);
243
-
244
- const hashmap = await redisClient.hGet(
245
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
246
- '/api/cached-static-fetch',
247
- );
248
- expect(hashmap).toBeNull();
249
- });
250
-
251
- it('A new request after the revalidation should increment the counter (because the route was re-evaluated)', async () => {
252
- const res = await fetch(NEXT_START_URL + '/api/cached-static-fetch');
253
- const data: any = await res.json();
254
- expect(data.counter).toBe(counter1 + 1);
255
- });
256
-
257
- it('After the new request was made the redis key and hashmap should be set again', async () => {
258
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
259
- // check Redis keys
260
- const keys = await redisClient.keys(
261
- process.env.VERCEL_URL + '/api/cached-static-fetch',
262
- );
263
- expect(keys.length).toBe(1);
264
-
265
- const hashmap = (await redisClient.hGet(
266
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
267
- '/api/cached-static-fetch',
268
- )) as string;
269
- expect(JSON.parse(hashmap)).toEqual([
270
- '_N_T_/layout',
271
- '_N_T_/api/layout',
272
- '_N_T_/api/cached-static-fetch/layout',
273
- '_N_T_/api/cached-static-fetch/route',
274
- '_N_T_/api/cached-static-fetch',
275
- ]);
276
- });
277
- });
278
-
279
- describe('should cache revalidation API routes in Redis', () => {
280
- let counter1: number;
281
-
282
- it('First request (should increment counter)', async () => {
283
- const res = await fetch(NEXT_START_URL + '/api/revalidated-fetch');
284
- const data: any = await res.json();
285
- expect(data.counter).toBe(1);
286
- counter1 = data.counter;
287
- });
288
-
289
- it('Second request which is send in revalidation time should hit cache (counter should not increment)', async () => {
290
- const res = await fetch(NEXT_START_URL + '/api/revalidated-fetch');
291
- const data: any = await res.json();
292
- expect(data.counter).toBe(counter1);
293
- });
294
-
295
- if (process.env.SKIP_OPTIONAL_LONG_RUNNER_TESTS !== 'true') {
296
- const FIRST_DELAY = 6000;
297
- const SECOND_DELAY = 1000;
298
- it('Third request which is send directly after revalidation time will still serve cache but trigger re-evaluation (stale-while-revalidate)', async () => {
299
- await delay(FIRST_DELAY);
300
- const res = await fetch(NEXT_START_URL + '/api/revalidated-fetch');
301
- const data: any = await res.json();
302
- expect(data.counter).toBe(counter1);
303
- }, 10_000);
304
-
305
- it('Third request which is send directly after revalidation time will serve re-evaluated data (stale-while-revalidate)', async () => {
306
- await delay(SECOND_DELAY);
307
- const res = await fetch(NEXT_START_URL + '/api/revalidated-fetch');
308
- const data: any = await res.json();
309
- expect(data.counter).toBe(counter1 + 1);
310
- });
311
-
312
- it('After expiration, the redis key should be removed from redis and the hashmap', async () => {
313
- const ttl = await redisClient.ttl(
314
- process.env.VERCEL_URL + '/api/revalidated-fetch',
315
- );
316
- expect(ttl).toBeLessThan(2 * revalidatedFetchRouteRevalidate);
317
- expect(ttl).toBeGreaterThan(
318
- 2 * revalidatedFetchRouteRevalidate -
319
- FIRST_DELAY -
320
- SECOND_DELAY -
321
- REDIS_BACKGROUND_SYNC_DELAY,
322
- );
323
-
324
- await delay(ttl * 1000 + 500);
325
-
326
- // check Redis keys
327
- const keys = await redisClient.keys(
328
- process.env.VERCEL_URL + '/api/revalidated-fetch',
329
- );
330
- expect(keys.length).toBe(0);
331
-
332
- await delay(1000);
333
-
334
- // The key should also be removed from the hashmap
335
- const hashmap = await redisClient.hGet(
336
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
337
- '/api/revalidated-fetch',
338
- );
339
- expect(hashmap).toBeNull();
340
- }, 15_000);
341
- }
342
- });
343
-
344
- // Next 16-only caching API tests. These routes exist only in the Next 16 test app
345
- // and exercise the new revalidateTag profiles and updateTag semantics.
346
- if (NEXT_TEST_APP.includes('16.')) {
347
- describe('Next 16 caching APIs', () => {
348
- const cachedStaticPath = '/api/cached-static-fetch';
349
-
350
- async function assertCachedStaticFetchCleared() {
351
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
352
- const keys = await redisClient.keys(
353
- process.env.VERCEL_URL + cachedStaticPath,
354
- );
355
- expect(keys.length).toBe(0);
356
-
357
- const hashmap = await redisClient.hGet(
358
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
359
- cachedStaticPath,
360
- );
361
- expect(hashmap).toBeNull();
362
- }
363
-
364
- it('revalidateTag(tag, "max") should invalidate cached-static-fetch by tag', async () => {
365
- // Warm up cache and sharedTagsMap for cached-static-fetch
366
- await fetch(NEXT_START_URL + cachedStaticPath);
367
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
368
-
369
- // Use explicit tag for this route as set by Next.js
370
- const tag = '_N_T_/api/cached-static-fetch';
371
- const res = await fetch(
372
- `${NEXT_START_URL}/api/revalidateTag?tag=${encodeURIComponent(
373
- tag,
374
- )}&profile=max`,
375
- );
376
- const json: any = await res.json();
377
- expect(json.success).toBe(true);
378
-
379
- await assertCachedStaticFetchCleared();
380
- });
381
-
382
- it('revalidateTag(tag, { expire: 60 }) should also invalidate cached-static-fetch', async () => {
383
- // Warm up cache again
384
- await fetch(NEXT_START_URL + cachedStaticPath);
385
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
386
-
387
- const tag = '_N_T_/api/cached-static-fetch';
388
- const res = await fetch(
389
- `${NEXT_START_URL}/api/revalidateTag?tag=${encodeURIComponent(
390
- tag,
391
- )}&profile=expire`,
392
- );
393
- const json: any = await res.json();
394
- expect(json.success).toBe(true);
395
-
396
- await assertCachedStaticFetchCleared();
397
- });
398
- });
399
- }
400
-
401
- describe('should not cache uncached API routes in Redis', () => {
402
- let counter1: number;
403
-
404
- it('First request should increment counter', async () => {
405
- const res1 = await fetch(NEXT_START_URL + '/api/uncached-fetch');
406
- const data1: any = await res1.json();
407
- expect(data1.counter).toBe(1);
408
- counter1 = data1.counter;
409
- });
410
-
411
- it('Second request should hit cache (counter should not increment if cache works)', async () => {
412
- const res2 = await fetch(NEXT_START_URL + '/api/uncached-fetch');
413
- const data2: any = await res2.json();
414
-
415
- // If not caching it is working request 2 should be higher as request one
416
- expect(data2.counter).toBe(counter1 + 1);
417
- });
418
-
419
- it('The redis key should not be set', async () => {
420
- // check the content of redis key
421
- const value = await redisClient.get(
422
- process.env.VERCEL_URL + '/api/uncached-fetch',
423
- );
424
- expect(value).toBeNull();
425
- });
426
- });
427
-
428
- describe('should cache a nested fetch request inside a uncached API route', () => {
429
- describe('should cache the nested fetch request (but not the API route itself)', () => {
430
- let counter: number;
431
- let subCounter: number;
432
-
433
- it('should deduplicate requests to the sub-fetch-request, but not to the API route itself', async () => {
434
- // make two requests, both should return the same subFetchData but different counter
435
- const res1 = await fetch(
436
- NEXT_START_URL + '/api/nested-fetch-in-api-route/revalidated-fetch',
437
- );
438
- const res2 = await fetch(
439
- NEXT_START_URL + '/api/nested-fetch-in-api-route/revalidated-fetch',
440
- );
441
- const [data1, data2]: any[] = await Promise.all([
442
- res1.json(),
443
- res2.json(),
444
- ]);
445
-
446
- // API route counter itself increments for each request
447
- // But we do not know which request is first and which is second
448
- if (data1.counter < data2.counter) {
449
- expect(data2.counter).toBeGreaterThan(data1.counter);
450
- counter = data2.counter;
451
- } else {
452
- expect(data1.counter).toBeGreaterThan(data2.counter);
453
- counter = data1.counter;
454
- }
455
-
456
- // API route counter of revalidated sub-fetch-request should be the same (request deduplication of fetch requests)
457
- expect(data1.subFetchData.counter).toBe(data2.subFetchData.counter);
458
- subCounter = data1.subFetchData.counter;
459
- });
460
-
461
- if (process.env.SKIP_OPTIONAL_LONG_RUNNER_TESTS !== 'true') {
462
- it('should return the same subFetchData after 2 seconds (regular caching within revalidation interval (=3s) works)', async () => {
463
- // make another request after 2 seconds, it should return the same subFetchData
464
- await delay(2000); // 2s < 3s (revalidate interval)
465
- const res = await fetch(
466
- NEXT_START_URL +
467
- '/api/nested-fetch-in-api-route/revalidated-fetch',
468
- );
469
- const data: any = await res.json();
470
-
471
- expect(data.counter).toBe(counter + 1);
472
- expect(data.subFetchData.counter).toBe(subCounter);
473
- });
474
-
475
- it('should return the same subFetchData after 2 seconds and new data after another 2 seconds (caching while revalidation works)', async () => {
476
- // make another request after another 2 seconds, it should return the same subFetchData (caching while revalidation works)
477
- await delay(2000); // 2s+2s < 3s*2 (=TTL = revalidate=3s*2)
478
- const res1 = await fetch(
479
- NEXT_START_URL +
480
- '/api/nested-fetch-in-api-route/revalidated-fetch',
481
- );
482
- const data1: any = await res1.json();
483
- expect(data1.counter).toBe(counter + 2);
484
- expect(data1.subFetchData.counter).toBe(subCounter);
485
-
486
- // make another request directly after first request which was still in TTL, it should return new data (caching while revalidation works)
487
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
488
- const res2 = await fetch(
489
- NEXT_START_URL +
490
- '/api/nested-fetch-in-api-route/revalidated-fetch',
491
- );
492
- const data2: any = await res2.json();
493
- expect(data2.counter).toBe(counter + 3);
494
- expect(data2.subFetchData.counter).toBe(subCounter + 1);
495
- });
496
-
497
- it('A request to revalidatePage API should remove the route from redis (string and hashmap)', async () => {
498
- const revalidateRes = await fetch(
499
- NEXT_START_URL +
500
- '/api/revalidatePath?path=/api/nested-fetch-in-api-route/revalidated-fetch',
501
- );
502
- const revalidateResJson: any = await revalidateRes.json();
503
- expect(revalidateResJson.success).toBe(true);
504
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
505
-
506
- // check Redis keys
507
- const keys = await redisClient.keys(
508
- process.env.VERCEL_URL +
509
- '/api/nested-fetch-in-api-route/revalidated-fetch',
510
- );
511
- expect(keys.length).toBe(0);
512
-
513
- const hashmap = await redisClient.hGet(
514
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
515
- '/api/nested-fetch-in-api-route/revalidated-fetch',
516
- );
517
- expect(hashmap).toBeNull();
518
- });
519
-
520
- it('A new request after the revalidation should increment the counter (because the route was re-evaluated)', async () => {
521
- const res = await fetch(
522
- NEXT_START_URL +
523
- '/api/nested-fetch-in-api-route/revalidated-fetch',
524
- );
525
- const data: any = await res.json();
526
- expect(data.counter).toBe(counter + 4);
527
- expect(data.subFetchData.counter).toBe(subCounter + 2);
528
- });
529
-
530
- it('After the new request was made the redis key and hashmap should be set again', async () => {
531
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
532
-
533
- const cacheEntryKey = await findFetchCacheEntryKey(
534
- 'revalidated-fetch-revalidate3-nested-fetch-in-api-route',
535
- );
536
-
537
- // check Redis keys
538
- const keys = await redisClient.keys(
539
- process.env.VERCEL_URL + cacheEntryKey,
540
- );
541
- expect(keys.length).toBe(1);
542
-
543
- const hashmap = (await redisClient.hGet(
544
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
545
- cacheEntryKey,
546
- )) as string;
547
- expect(JSON.parse(hashmap)).toEqual([
548
- 'revalidated-fetch-revalidate3-nested-fetch-in-api-route',
549
- ]);
550
- });
551
-
552
- it('A request to revalidateTag API should remove the route from redis (string and hashmap)', async () => {
553
- const revalidateRes = await fetch(
554
- NEXT_START_URL +
555
- '/api/revalidateTag?tag=revalidated-fetch-revalidate3-nested-fetch-in-api-route',
556
- );
557
- const revalidateResJson: any = await revalidateRes.json();
558
- expect(revalidateResJson.success).toBe(true);
559
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
560
-
561
- // check Redis keys
562
- const keys = await redisClient.keys(
563
- process.env.VERCEL_URL +
564
- '/api/nested-fetch-in-api-route/revalidated-fetch',
565
- );
566
- expect(keys.length).toBe(0);
567
-
568
- const hashmap = await redisClient.hGet(
569
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
570
- '/api/nested-fetch-in-api-route/revalidated-fetch',
571
- );
572
- expect(hashmap).toBeNull();
573
- });
574
-
575
- it('Another new request after the revalidation should increment the counter (because the route was re-evaluated)', async () => {
576
- const res = await fetch(
577
- NEXT_START_URL +
578
- '/api/nested-fetch-in-api-route/revalidated-fetch',
579
- );
580
- const data: any = await res.json();
581
- expect(data.counter).toBe(counter + 5);
582
- expect(data.subFetchData.counter).toBe(subCounter + 3);
583
- });
584
-
585
- it('After the new request was made the redis key and hashmap should be set again', async () => {
586
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
587
- const cacheEntryKey = await findFetchCacheEntryKey(
588
- 'revalidated-fetch-revalidate3-nested-fetch-in-api-route',
589
- );
590
-
591
- // check Redis keys
592
- const keys = await redisClient.keys(
593
- process.env.VERCEL_URL + cacheEntryKey,
594
- );
595
- expect(keys.length).toBe(1);
596
-
597
- const hashmap = (await redisClient.hGet(
598
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
599
- cacheEntryKey,
600
- )) as string;
601
- expect(JSON.parse(hashmap)).toEqual([
602
- 'revalidated-fetch-revalidate3-nested-fetch-in-api-route',
603
- ]);
604
- });
605
- }
606
- });
607
- });
608
-
609
- // describe('With a API route that has a unstable_cacheTag', () => {
610
- // // TODO: implement API route for this test as well as the test itself
611
- // });
612
-
613
- // describe('With a API route that has a unstable_cacheLife', () => {
614
- // // TODO: implement API route for this test as well as the test itself
615
- // });
616
- });
617
-
618
- describe('should have the correct caching behavior for pages', () => {
619
- describe('Without any fetch requests inside the page', () => {
620
- describe('With default page configuration for revalidate and dynamic values', () => {
621
- let timestamp1: string | undefined;
622
-
623
- it('Two parallel requests should return the same timestamp (because requests are deduplicated)', async () => {
624
- // First request (should increment counter)
625
- const [pageRes1, pageRes2] = await Promise.all([
626
- fetch(NEXT_START_URL + '/pages/no-fetch/default-page'),
627
- fetch(NEXT_START_URL + '/pages/no-fetch/default-page'),
628
- ]);
629
-
630
- const pageText1 = await pageRes1.text();
631
- timestamp1 = pageText1.match(/Timestamp: <!-- -->(\d+)/)?.[1];
632
- expect(timestamp1).toBeDefined();
633
-
634
- const pageText2 = await pageRes2.text();
635
- const timestamp2 = pageText2.match(/Timestamp: <!-- -->(\d+)/)?.[1];
636
- expect(timestamp2).toBeDefined();
637
- expect(timestamp1).toBe(timestamp2);
638
- });
639
-
640
- it('Redis should have a key for the page which should have a TTL set to 28 days (2 * 14 days default revalidate time)', async () => {
641
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
642
- // check Redis keys
643
- const ttl = await redisClient.ttl(
644
- process.env.VERCEL_URL + '/pages/no-fetch/default-page',
645
- );
646
- // 14 days is default revalidate for pages -> expiration time is 2 * revalidate time -> -10 seconds for testing offset stability
647
- expect(ttl).toBeGreaterThan(2 * 14 * 24 * 60 * 60 - 30);
648
- expect(ttl).toBeLessThanOrEqual(2 * 14 * 24 * 60 * 60);
649
- });
650
-
651
- it('The data in the redis key should match the expected format', async () => {
652
- const data = (await redisClient.get(
653
- process.env.VERCEL_URL + '/pages/no-fetch/default-page',
654
- )) as string;
655
- expect(data).toBeDefined();
656
- const cacheEntry: CacheEntry = JSON.parse(data);
657
-
658
- // The format should be as expected. We intentionally do not assert on an optional status field here
659
- // so that different Next.js versions (which may include or omit it) are both supported.
660
- expect(cacheEntry).toMatchObject({
661
- value: {
662
- kind: 'APP_PAGE',
663
- html: expect.any(String),
664
- rscData: {
665
- $binary: expect.any(String),
666
- },
667
- headers: {
668
- 'x-nextjs-stale-time': expect.any(String),
669
- 'x-next-cache-tags':
670
- '_N_T_/layout,_N_T_/pages/layout,_N_T_/pages/no-fetch/layout,_N_T_/pages/no-fetch/default-page/layout,_N_T_/pages/no-fetch/default-page/page,_N_T_/pages/no-fetch/default-page',
671
- },
672
- },
673
- lastModified: expect.any(Number),
674
- tags: [
675
- '_N_T_/layout',
676
- '_N_T_/pages/layout',
677
- '_N_T_/pages/no-fetch/layout',
678
- '_N_T_/pages/no-fetch/default-page/layout',
679
- '_N_T_/pages/no-fetch/default-page/page',
680
- '_N_T_/pages/no-fetch/default-page',
681
- ],
682
- });
683
- });
684
-
685
- if (process.env.SKIP_OPTIONAL_LONG_RUNNER_TESTS !== 'true') {
686
- it('A new request after 3 seconds should return the same timestamp (because the page was cached in in-memory cache)', async () => {
687
- await delay(3_000);
688
- const pageRes3 = await fetch(
689
- NEXT_START_URL + '/pages/no-fetch/default-page',
690
- );
691
- const pageText3 = await pageRes3.text();
692
- const timestamp3 = pageText3.match(/Timestamp: <!-- -->(\d+)/)?.[1];
693
- expect(timestamp3).toBeDefined();
694
- expect(timestamp1).toBe(timestamp3);
695
- });
696
-
697
- it('A new request after 11 seconds should return the same timestamp (because the page was cached in redis cache)', async () => {
698
- await delay(11_000);
699
- const pageRes4 = await fetch(
700
- NEXT_START_URL + '/pages/no-fetch/default-page',
701
- );
702
- const pageText4 = await pageRes4.text();
703
- const timestamp4 = pageText4.match(/Timestamp: <!-- -->(\d+)/)?.[1];
704
- expect(timestamp4).toBeDefined();
705
- expect(timestamp1).toBe(timestamp4);
706
- }, 15_000);
707
- }
708
-
709
- it('A request to revalidatePage API should remove the page from redis (string and hashmap)', async () => {
710
- const revalidateRes = await fetch(
711
- NEXT_START_URL +
712
- '/api/revalidatePath?path=/pages/no-fetch/default-page',
713
- );
714
- const revalidateResJson: any = await revalidateRes.json();
715
- expect(revalidateResJson.success).toBe(true);
716
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
717
-
718
- // check Redis keys
719
- const keys = await redisClient.keys(
720
- process.env.VERCEL_URL + '/pages/no-fetch/default-page',
721
- );
722
- expect(keys.length).toBe(0);
723
-
724
- const hashmap = await redisClient.hGet(
725
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
726
- '/pages/no-fetch/default-page',
727
- );
728
- expect(hashmap).toBeNull();
729
- });
730
-
731
- it('A new request after the revalidation should return a new timestamp (because the page was recreated)', async () => {
732
- const pageRes4 = await fetch(
733
- NEXT_START_URL + '/pages/no-fetch/default-page',
734
- );
735
- const pageText4 = await pageRes4.text();
736
- const timestamp4 = pageText4.match(/Timestamp: <!-- -->(\d+)/)?.[1];
737
- expect(timestamp4).toBeDefined();
738
- expect(Number(timestamp4)).toBeGreaterThan(Number(timestamp1));
739
- });
740
-
741
- it('After the new request was made the redis key and hashmap should be set again', async () => {
742
- // check Redis keys
743
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
744
- const keys = await redisClient.keys(
745
- process.env.VERCEL_URL + '/pages/no-fetch/default-page',
746
- );
747
- expect(keys.length).toBe(1);
748
-
749
- const hashmap = (await redisClient.hGet(
750
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
751
- '/pages/no-fetch/default-page',
752
- )) as string;
753
- expect(JSON.parse(hashmap)).toEqual([
754
- '_N_T_/layout',
755
- '_N_T_/pages/layout',
756
- '_N_T_/pages/no-fetch/layout',
757
- '_N_T_/pages/no-fetch/default-page/layout',
758
- '_N_T_/pages/no-fetch/default-page/page',
759
- '_N_T_/pages/no-fetch/default-page',
760
- ]);
761
- });
762
- });
763
- });
764
-
765
- // describe('With a cached static fetch request inside a page', () => {
766
- // // TODO: implement test for `test/nextjs-test-projects/next-app/src/app/pages/cached-static-fetch`
767
- // });
768
-
769
- describe('With a cached revalidation fetch request inside a page', () => {
770
- let firstTimestamp: string;
771
- let firstCounter: string;
772
-
773
- it('should set all cache entries for this page after request is finished', async () => {
774
- const pageRes = await fetch(
775
- NEXT_START_URL +
776
- '/pages/revalidated-fetch/revalidate15--default-page',
777
- );
778
- const pageText = await pageRes.text();
779
- const timestamp = pageText.match(/Timestamp: <!-- -->(\d+)/)?.[1];
780
- const counter = pageText.match(/Counter: <!-- -->(\d+)/)?.[1];
781
- expect(timestamp).toBeDefined();
782
- expect(counter).toBeDefined();
783
- firstTimestamp = timestamp!;
784
- firstCounter = counter!;
785
-
786
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
787
-
788
- const nestedFetchCacheEntryKey = await findFetchCacheEntryKey(
789
- 'revalidated-fetch-revalidate15-default-page',
790
- );
791
-
792
- // test cache entry for 3 keys are set
793
- const keys1 = await redisClient.keys(
794
- process.env.VERCEL_URL +
795
- '/pages/revalidated-fetch/revalidate15--default-page',
796
- );
797
- expect(keys1.length).toBe(1);
798
- const keys2 = await redisClient.keys(
799
- process.env.VERCEL_URL + nestedFetchCacheEntryKey,
800
- );
801
- expect(keys2.length).toBe(1);
802
- const keys3 = await redisClient.keys(
803
- process.env.VERCEL_URL + '/api/revalidated-fetch',
804
- );
805
- expect(keys3.length).toBe(1);
806
-
807
- // test shared tag hashmap to be set for all keys
808
- const hashmap1 = (await redisClient.hGet(
809
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
810
- '/pages/revalidated-fetch/revalidate15--default-page',
811
- )) as string;
812
- expect(JSON.parse(hashmap1)).toEqual([
813
- '_N_T_/layout',
814
- '_N_T_/pages/layout',
815
- '_N_T_/pages/revalidated-fetch/layout',
816
- '_N_T_/pages/revalidated-fetch/revalidate15--default-page/layout',
817
- '_N_T_/pages/revalidated-fetch/revalidate15--default-page/page',
818
- '_N_T_/pages/revalidated-fetch/revalidate15--default-page',
819
- 'revalidated-fetch-revalidate15-default-page',
820
- ]);
821
- const hashmap2 = (await redisClient.hGet(
822
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
823
- nestedFetchCacheEntryKey,
824
- )) as string;
825
- expect(JSON.parse(hashmap2)).toEqual([
826
- 'revalidated-fetch-revalidate15-default-page',
827
- ]);
828
- const hashmap3 = (await redisClient.hGet(
829
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
830
- '/api/revalidated-fetch',
831
- )) as string;
832
- expect(JSON.parse(hashmap3)).toEqual([
833
- '_N_T_/layout',
834
- '_N_T_/api/layout',
835
- '_N_T_/api/revalidated-fetch/layout',
836
- '_N_T_/api/revalidated-fetch/route',
837
- '_N_T_/api/revalidated-fetch',
838
- ]);
839
- });
840
-
841
- it('a new request should return the same timestamp as the first request', async () => {
842
- const pageRes = await fetch(
843
- NEXT_START_URL +
844
- '/pages/revalidated-fetch/revalidate15--default-page',
845
- );
846
- const pageText = await pageRes.text();
847
- const timestamp = pageText.match(/Timestamp: <!-- -->(\d+)/)?.[1];
848
- expect(timestamp).toBeDefined();
849
- expect(timestamp).toBe(firstTimestamp);
850
- });
851
-
852
- it('A request to revalidatePath API should remove the page from redis (string and hashmap) but not the api route', async () => {
853
- const revalidateRes = await fetch(
854
- NEXT_START_URL +
855
- '/api/revalidatePath?path=/pages/revalidated-fetch/revalidate15--default-page',
856
- );
857
- const revalidateResJson: any = await revalidateRes.json();
858
- expect(revalidateResJson.success).toBe(true);
859
- await delay(REDIS_BACKGROUND_SYNC_DELAY);
860
-
861
- // test no cache entry for 2 keys
862
- const keys1 = await redisClient.keys(
863
- process.env.VERCEL_URL +
864
- '/pages/revalidated-fetch/revalidate15--default-page',
865
- );
866
- expect(keys1.length).toBe(0);
867
-
868
- const hashmap1 = await redisClient.hGet(
869
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
870
- '/pages/revalidated-fetch/revalidate15--default-page',
871
- );
872
- expect(hashmap1).toBeNull();
873
-
874
- // sub-fetch-request is not removed directly but will be removed on next get request
875
- const nestedFetchCacheEntryKey = await findFetchCacheEntryKey(
876
- 'revalidated-fetch-revalidate15-default-page',
877
- );
878
- const keys2 = await redisClient.keys(
879
- process.env.VERCEL_URL + nestedFetchCacheEntryKey,
880
- );
881
- expect(keys2.length).toBe(1);
882
- const hashmap2 = await redisClient.hGet(
883
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
884
- nestedFetchCacheEntryKey,
885
- );
886
- expect(hashmap2).toBeDefined();
887
-
888
- // the page should also be in revalidatedTagsMap so that the nested fetch requests knows that the page was invalidated
889
- const revalidationTimestamp = await redisClient.hGet(
890
- process.env.VERCEL_URL + '__revalidated_tags__',
891
- '_N_T_/pages/revalidated-fetch/revalidate15--default-page',
892
- );
893
- const ts = Number(revalidationTimestamp);
894
- expect(ts).toBeGreaterThan(1);
895
- expect(ts).toBeLessThan(Number(Date.now()));
896
-
897
- // API route should still be cached
898
- const keys3 = await redisClient.keys(
899
- process.env.VERCEL_URL + '/api/revalidated-fetch',
900
- );
901
- expect(keys3.length).toBe(1);
902
- const hashmap3 = (await redisClient.hGet(
903
- process.env.VERCEL_URL + SHARED_TAGS_KEY,
904
- '/api/revalidated-fetch',
905
- )) as string;
906
- expect(JSON.parse(hashmap3)).toEqual([
907
- '_N_T_/layout',
908
- '_N_T_/api/layout',
909
- '_N_T_/api/revalidated-fetch/layout',
910
- '_N_T_/api/revalidated-fetch/route',
911
- '_N_T_/api/revalidated-fetch',
912
- ]);
913
- });
914
-
915
- it('a new request should return a newer timestamp as the first request (which was invalidated by revalidatePath)', async () => {
916
- const pageRes = await fetch(
917
- NEXT_START_URL +
918
- '/pages/revalidated-fetch/revalidate15--default-page',
919
- );
920
- const pageText = await pageRes.text();
921
- const timestamp = pageText.match(/Timestamp: <!-- -->(\d+)/)?.[1];
922
- const secondCounter = pageText.match(/Counter: <!-- -->(\d+)/)?.[1];
923
- expect(timestamp).toBeDefined();
924
- expect(Number(timestamp)).toBeGreaterThan(Number(firstTimestamp));
925
-
926
- //but the new request should not have a higher counter than the first request (because the cache of the API route should not be invalidated)
927
- expect(secondCounter).toBeDefined();
928
- expect(secondCounter).toBe(firstCounter);
929
- });
930
- });
931
-
932
- // describe('With a uncached fetch request inside a page', () => {
933
- // // TODO: implement test for `test/nextjs-test-projects/next-app/src/app/pages/uncached-fetch`
934
- //
935
- // });
936
-
937
- // describe('With a page that has a unstable_cacheTag', () => {
938
- // // TODO: implement page for this test as well as the test itself
939
- // });
940
-
941
- // describe('With a page that has a unstable_cacheLife', () => {
942
- // // TODO: implement page for this test as well as the test itself
943
- // });
944
- });
945
- });