@trieb.work/nextjs-turbo-redis-cache 1.15.0 → 1.15.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 (246) hide show
  1. package/.cursor/skills/testing/SKILL.md +121 -0
  2. package/.github/workflows/ci.yml +155 -70
  3. package/ARCHITECTURE.md +431 -0
  4. package/CHANGELOG.md +7 -0
  5. package/README.md +72 -47
  6. package/dist/index.js +78 -14
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +78 -14
  9. package/dist/index.mjs.map +1 -1
  10. package/eslint.config.mjs +1 -1
  11. package/package.json +14 -16
  12. package/playwright.config.ts +5 -3
  13. package/src/CacheComponentsHandler.ts +57 -2
  14. package/src/SyncedMap.ts +62 -13
  15. package/test/README.md +179 -0
  16. package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/package.json +6 -6
  17. package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-15-4-11}/pnpm-lock.yaml +89 -105
  18. package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/package.json +3 -3
  19. package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/pnpm-lock.yaml +53 -53
  20. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/package.json +28 -0
  21. package/test/nextjs-test-projects/next-app-16-0-11-cache-components/pnpm-lock.yaml +4206 -0
  22. package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +18 -0
  23. package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/package.json +3 -3
  24. package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/pnpm-lock.yaml +54 -60
  25. package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +7 -0
  26. package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
  27. package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-2-6}/tsconfig.json +9 -2
  28. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +3 -0
  29. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +13 -0
  30. package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6-cache-components}/package.json +3 -3
  31. package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6-cache-components}/pnpm-lock.yaml +54 -60
  32. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +19 -0
  33. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +24 -0
  34. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +21 -0
  35. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +19 -0
  36. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +19 -0
  37. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +110 -0
  38. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +90 -0
  39. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +127 -0
  40. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +130 -0
  41. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +127 -0
  42. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +110 -0
  43. package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/layout.tsx +0 -2
  44. package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +22 -0
  45. package/{tests → test/playwright}/update-tag.spec.ts +1 -1
  46. package/test/{integration → vitest/integration}/build-id-prefix.integration.test.ts +2 -2
  47. package/test/{cache-components/cache-components.integration.spec.ts → vitest/integration/cache-components/cache-components.integration.test.ts} +7 -3
  48. package/test/{cache-components/redis-kill-reconnect.spec.ts → vitest/integration/cache-components/redis-kill-reconnect.test.ts} +28 -7
  49. package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +59 -0
  50. package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +100 -0
  51. package/test/{cache-components → vitest/integration/cache-components}/scripts/redis-kill-reconnect.ts +31 -8
  52. package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +113 -0
  53. package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +379 -0
  54. package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +155 -0
  55. package/test/{integration → vitest/integration}/nextjs-cache-handler.integration.test.ts +15 -9
  56. package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +154 -0
  57. package/{src → test/vitest/unit}/index.test.ts +3 -3
  58. package/test/{cache-components/reconnect-socket-already-opened.spec.ts → vitest/unit/reconnect-socket-already-opened.test.ts} +1 -1
  59. package/{src → test/vitest/unit}/serializer.test.ts +7 -3
  60. package/{src → test/vitest/unit}/utils/prefix.test.ts +1 -1
  61. package/vite.config.ts +4 -3
  62. package/vitest.cache-components.config.ts +1 -1
  63. package/test/browser/update-tag.browser.test.ts +0 -41
  64. package/test/integration/next-app-15-0-3/next.config.js +0 -6
  65. package/test/integration/next-app-15-0-3/package-lock.json +0 -5833
  66. package/test/integration/next-app-15-0-3/pnpm-lock.yaml +0 -3710
  67. package/test/integration/next-app-15-3-2/next.config.js +0 -6
  68. package/test/integration/next-app-15-3-2/package.json +0 -33
  69. package/test/integration/next-app-15-3-2/pnpm-lock.yaml +0 -3720
  70. package/test/integration/next-app-15-3-2/postcss.config.mjs +0 -5
  71. package/test/integration/next-app-15-3-2/src/app/api/revalidateTag/route.ts +0 -15
  72. package/test/integration/next-app-15-4-7/eslint.config.mjs +0 -16
  73. package/test/integration/next-app-15-4-7/package-lock.json +0 -5969
  74. package/test/integration/next-app-15-4-7/package.json +0 -33
  75. package/test/integration/next-app-15-4-7/postcss.config.mjs +0 -5
  76. package/test/integration/next-app-15-4-7/src/app/api/revalidateTag/route.ts +0 -15
  77. package/test/integration/next-app-15-4-7/tsconfig.json +0 -27
  78. package/test/integration/next-app-16-0-3/src/app/api/cached-static-fetch/route.ts +0 -18
  79. package/test/integration/next-app-16-0-3/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  80. package/test/integration/next-app-16-0-3/src/app/api/revalidatePath/route.ts +0 -15
  81. package/test/integration/next-app-16-0-3/src/app/api/revalidated-fetch/route.ts +0 -17
  82. package/test/integration/next-app-16-0-3/src/app/api/uncached-fetch/route.ts +0 -15
  83. package/test/integration/next-app-16-0-3/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  84. package/test/integration/next-app-16-0-3/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  85. package/test/integration/next-app-16-0-3/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  86. package/test/integration/next-app-16-0-3/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  87. package/test/integration/next-app-16-0-3/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  88. package/test/integration/next-app-16-0-3/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  89. package/test/integration/next-app-16-0-3/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  90. package/test/integration/next-app-16-0-3/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  91. package/test/integration/next-app-16-0-3/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  92. package/test/integration/next-app-16-0-3/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  93. package/test/integration/next-app-16-2-3/src/app/api/cached-static-fetch/route.ts +0 -18
  94. package/test/integration/next-app-16-2-3/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
  95. package/test/integration/next-app-16-2-3/src/app/api/revalidatePath/route.ts +0 -15
  96. package/test/integration/next-app-16-2-3/src/app/api/revalidated-fetch/route.ts +0 -17
  97. package/test/integration/next-app-16-2-3/src/app/api/uncached-fetch/route.ts +0 -15
  98. package/test/integration/next-app-16-2-3/src/app/globals.css +0 -26
  99. package/test/integration/next-app-16-2-3/src/app/layout.tsx +0 -59
  100. package/test/integration/next-app-16-2-3/src/app/page.tsx +0 -755
  101. package/test/integration/next-app-16-2-3/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
  102. package/test/integration/next-app-16-2-3/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
  103. package/test/integration/next-app-16-2-3/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  104. package/test/integration/next-app-16-2-3/src/app/pages/no-fetch/default-page/page.tsx +0 -55
  105. package/test/integration/next-app-16-2-3/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
  106. package/test/integration/next-app-16-2-3/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
  107. package/test/integration/next-app-16-2-3/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  108. package/test/integration/next-app-16-2-3/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
  109. package/test/integration/next-app-16-2-3/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
  110. package/test/integration/next-app-16-2-3/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
  111. package/test/integration/next-app-16-2-3/src/app/revalidation-interface.tsx +0 -267
  112. package/test/integration/next-app-16-2-3-cache-components/public/file.svg +0 -1
  113. package/test/integration/next-app-16-2-3-cache-components/public/globe.svg +0 -1
  114. package/test/integration/next-app-16-2-3-cache-components/public/next.svg +0 -1
  115. package/test/integration/next-app-16-2-3-cache-components/public/vercel.svg +0 -1
  116. package/test/integration/next-app-16-2-3-cache-components/public/window.svg +0 -1
  117. package/test/integration/next-app-customized/README.md +0 -36
  118. package/test/integration/next-app-customized/eslint.config.mjs +0 -16
  119. package/vitest.browser.config.ts +0 -10
  120. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/README.md +0 -0
  121. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/eslint.config.mjs +0 -0
  122. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-15-4-11}/next.config.js +0 -0
  123. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-15-4-11}/package-lock.json +0 -0
  124. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/postcss.config.mjs +0 -0
  125. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/file.svg +0 -0
  126. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/globe.svg +0 -0
  127. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/next.svg +0 -0
  128. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/vercel.svg +0 -0
  129. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/public/window.svg +0 -0
  130. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/cached-static-fetch/route.ts +0 -0
  131. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -0
  132. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/revalidatePath/route.ts +0 -0
  133. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/revalidateTag/route.ts +0 -0
  134. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/revalidated-fetch/route.ts +0 -0
  135. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/api/uncached-fetch/route.ts +0 -0
  136. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/globals.css +0 -0
  137. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/layout.tsx +0 -0
  138. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/page.tsx +0 -0
  139. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -0
  140. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -0
  141. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  142. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/no-fetch/default-page/page.tsx +0 -0
  143. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -0
  144. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -0
  145. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  146. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -0
  147. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -0
  148. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  149. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/src/app/revalidation-interface.tsx +0 -0
  150. /package/test/{integration/next-app-15-0-3 → nextjs-test-projects/next-app-15-4-11}/tsconfig.json +0 -0
  151. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/README.md +0 -0
  152. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/eslint.config.mjs +0 -0
  153. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/next.config.ts +0 -0
  154. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/postcss.config.mjs +0 -0
  155. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/cached-static-fetch/route.ts +0 -0
  156. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -0
  157. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/revalidatePath/route.ts +0 -0
  158. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/src/app/api/revalidateTag/route.ts +0 -0
  159. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/revalidated-fetch/route.ts +0 -0
  160. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/api/uncached-fetch/route.ts +0 -0
  161. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/src/app/favicon.ico +0 -0
  162. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/src/app/globals.css +0 -0
  163. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/layout.tsx +0 -0
  164. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/src/app/page.tsx +0 -0
  165. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -0
  166. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -0
  167. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  168. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/no-fetch/default-page/page.tsx +0 -0
  169. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -0
  170. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -0
  171. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  172. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -0
  173. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -0
  174. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11}/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  175. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-0-11}/src/app/revalidation-interface.tsx +0 -0
  176. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11}/src/app/update-tag-test/page.tsx +0 -0
  177. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11}/tsconfig.json +0 -0
  178. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/README.md +0 -0
  179. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/cache-handler.js +0 -0
  180. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/eslint.config.mjs +0 -0
  181. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/next.config.ts +0 -0
  182. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/postcss.config.mjs +0 -0
  183. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/file.svg +0 -0
  184. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/globe.svg +0 -0
  185. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/next.svg +0 -0
  186. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/vercel.svg +0 -0
  187. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/public/window.svg +0 -0
  188. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/cached-static-fetch/route.ts +0 -0
  189. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/cached-with-cachelife/route.ts +0 -0
  190. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/cached-with-tag/route.ts +0 -0
  191. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/revalidate-tag/route.ts +0 -0
  192. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/api/revalidated-fetch/route.ts +0 -0
  193. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/cachelife-short/page.tsx +0 -0
  194. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/page.tsx +0 -0
  195. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -0
  196. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -0
  197. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/tag-invalidation/page.tsx +0 -0
  198. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -0
  199. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/favicon.ico +0 -0
  200. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/globals.css +0 -0
  201. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/layout.tsx +0 -0
  202. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/page.tsx +0 -0
  203. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/revalidation-interface.tsx +0 -0
  204. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-0-11-cache-components}/src/app/update-tag-test/page.tsx +0 -0
  205. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-0-11-cache-components}/tsconfig.json +0 -0
  206. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/README.md +0 -0
  207. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/next.config.ts +0 -0
  208. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/cached-static-fetch/route.ts +0 -0
  209. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -0
  210. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/revalidatePath/route.ts +0 -0
  211. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/src/app/api/revalidateTag/route.ts +0 -0
  212. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/revalidated-fetch/route.ts +0 -0
  213. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/api/uncached-fetch/route.ts +0 -0
  214. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/src/app/globals.css +0 -0
  215. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/layout.tsx +0 -0
  216. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/src/app/page.tsx +0 -0
  217. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -0
  218. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -0
  219. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  220. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/no-fetch/default-page/page.tsx +0 -0
  221. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -0
  222. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -0
  223. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  224. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -0
  225. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -0
  226. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6}/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  227. /package/test/{integration/next-app-16-2-3-cache-components → nextjs-test-projects/next-app-16-2-6}/src/app/revalidation-interface.tsx +0 -0
  228. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6}/src/app/update-tag-test/page.tsx +0 -0
  229. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/README.md +0 -0
  230. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/eslint.config.mjs +0 -0
  231. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/postcss.config.mjs +0 -0
  232. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/file.svg +0 -0
  233. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/globe.svg +0 -0
  234. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/next.svg +0 -0
  235. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/vercel.svg +0 -0
  236. /package/test/{integration/next-app-15-4-7 → nextjs-test-projects/next-app-16-2-6-cache-components}/public/window.svg +0 -0
  237. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/favicon.ico +0 -0
  238. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/globals.css +0 -0
  239. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/page.tsx +0 -0
  240. /package/test/{integration/next-app-16-0-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/src/app/revalidation-interface.tsx +0 -0
  241. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-16-2-6-cache-components}/tsconfig.json +0 -0
  242. /package/test/{integration/next-app-16-2-3 → nextjs-test-projects/next-app-customized}/README.md +0 -0
  243. /package/test/{integration → nextjs-test-projects}/next-app-customized/customized-cache-handler.js +0 -0
  244. /package/test/{integration/next-app-15-3-2 → nextjs-test-projects/next-app-customized}/eslint.config.mjs +0 -0
  245. /package/test/{integration → nextjs-test-projects}/next-app-customized/next.config.js +0 -0
  246. /package/{tests → test/playwright}/cache-lab.spec.ts +0 -0
package/dist/index.mjs CHANGED
@@ -26,6 +26,7 @@ function debugVerbose(color, ...args) {
26
26
  var SYNC_CHANNEL_SUFFIX = ":sync-channel:";
27
27
  var SyncedMap = class {
28
28
  constructor(options) {
29
+ this.isReconnecting = false;
29
30
  this.client = options.client;
30
31
  this.keyPrefix = options.keyPrefix;
31
32
  this.redisKey = options.redisKey;
@@ -166,6 +167,10 @@ var SyncedMap = class {
166
167
  }
167
168
  };
168
169
  try {
170
+ this.subscriberClient.on("error", async (err) => {
171
+ console.error("Subscriber client error:", err);
172
+ await this.reconnectSubscriber();
173
+ });
169
174
  const connectIfNeeded = async () => {
170
175
  if (!this.subscriberClient.isOpen && !this.subscriberClient.isReady) {
171
176
  await this.subscriberClient.connect();
@@ -185,12 +190,12 @@ var SyncedMap = class {
185
190
  const keyspaceEventConfig = (await this.subscriberClient.configGet("notify-keyspace-events"))?.["notify-keyspace-events"];
186
191
  if (!keyspaceEventConfig.includes("E")) {
187
192
  throw new Error(
188
- 'Keyspace event configuration is set to "' + keyspaceEventConfig + "\" but has to include 'E' for Keyevent events, published with __keyevent@<db>__ prefix. We recommend to set it to 'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`"
193
+ 'Keyspace event configuration is set to "' + keyspaceEventConfig + " but has to include 'E' for Keyevent events, published with __keyevent@<db>__ prefix. We recommend to set it to 'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`"
189
194
  );
190
195
  }
191
196
  if (!keyspaceEventConfig.includes("A") && !(keyspaceEventConfig.includes("x") && keyspaceEventConfig.includes("e"))) {
192
197
  throw new Error(
193
- 'Keyspace event configuration is set to "' + keyspaceEventConfig + "\" but has to include 'A' or 'x' and 'e' for expired and evicted events. We recommend to set it to 'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`"
198
+ 'Keyspace event configuration is set to "' + keyspaceEventConfig + " but has to include 'A' or 'x' and 'e' for expired and evicted events. We recommend to set it to 'Exe' like so `redis-cli -h localhost config set notify-keyspace-events Exe`"
194
199
  );
195
200
  }
196
201
  }
@@ -210,22 +215,48 @@ var SyncedMap = class {
210
215
  keyEventHandler
211
216
  )
212
217
  ]);
213
- this.subscriberClient.on("error", async (err) => {
214
- console.error("Subscriber client error:", err);
218
+ } catch (error) {
219
+ console.error("Error setting up pub/sub client:", error);
220
+ throw error;
221
+ }
222
+ }
223
+ /**
224
+ * Reconnect the subscriber client with exponential backoff.
225
+ * Guarded by `isReconnecting` to prevent concurrent reconnection
226
+ * attempts when multiple error events fire during an outage.
227
+ * Retries indefinitely (with capped delay) so the subscriber
228
+ * eventually recovers once Redis is available again.
229
+ */
230
+ async reconnectSubscriber() {
231
+ if (this.isReconnecting) return;
232
+ this.isReconnecting = true;
233
+ try {
234
+ let attempt = 0;
235
+ while (true) {
215
236
  try {
216
- await this.subscriberClient.quit();
237
+ try {
238
+ this.subscriberClient.removeAllListeners("error");
239
+ await this.subscriberClient.disconnect();
240
+ } catch {
241
+ }
217
242
  this.subscriberClient = this.client.duplicate();
218
243
  await this.setupPubSub();
219
- } catch (reconnectError) {
244
+ return;
245
+ } catch (error) {
246
+ const delay = Math.min(
247
+ 500 * Math.pow(2, Math.min(attempt, 5)),
248
+ 1e4
249
+ );
250
+ attempt++;
220
251
  console.error(
221
- "Failed to reconnect subscriber client:",
222
- reconnectError
252
+ `Subscriber reconnect attempt ${attempt} failed, retrying in ${delay}ms`,
253
+ error
223
254
  );
255
+ await new Promise((r) => setTimeout(r, delay));
224
256
  }
225
- });
226
- } catch (error) {
227
- console.error("Error setting up pub/sub client:", error);
228
- throw error;
257
+ }
258
+ } finally {
259
+ this.isReconnecting = false;
229
260
  }
230
261
  }
231
262
  async waitUntilReady() {
@@ -1068,6 +1099,8 @@ var RedisCacheComponentsHandler = class {
1068
1099
  getTimeoutMs = process.env.REDIS_COMMAND_TIMEOUT_MS ? Number.parseInt(process.env.REDIS_COMMAND_TIMEOUT_MS) ?? 500 : 500,
1069
1100
  revalidateTagQuerySize = 250,
1070
1101
  avgResyncIntervalMs = 60 * 60 * 1e3,
1102
+ redisGetDeduplication = true,
1103
+ inMemoryCachingTime = 1e4,
1071
1104
  socketOptions,
1072
1105
  clientOptions,
1073
1106
  killContainerOnErrorThreshold = process.env.KILL_CONTAINER_ON_ERROR_THRESHOLD ? Number.parseInt(process.env.KILL_CONTAINER_ON_ERROR_THRESHOLD) ?? 0 : 0,
@@ -1080,6 +1113,8 @@ var RedisCacheComponentsHandler = class {
1080
1113
  env: process.env
1081
1114
  });
1082
1115
  this.getTimeoutMs = getTimeoutMs;
1116
+ this.redisGetDeduplication = redisGetDeduplication;
1117
+ this.inMemoryCachingTime = inMemoryCachingTime;
1083
1118
  this.client = createClient2({
1084
1119
  url: redisUrl,
1085
1120
  pingInterval: 1e4,
@@ -1146,6 +1181,26 @@ var RedisCacheComponentsHandler = class {
1146
1181
  filterKeys,
1147
1182
  resyncIntervalMs: avgResyncIntervalMs - avgResyncIntervalMs / 10 + Math.random() * (avgResyncIntervalMs / 10)
1148
1183
  });
1184
+ this.inMemoryDeduplicationCache = new SyncedMap({
1185
+ client: this.client,
1186
+ keyPrefix: this.keyPrefix,
1187
+ redisKey: "__cacheComponents_inMemoryDeduplicationCache__",
1188
+ database,
1189
+ querySize: revalidateTagQuerySize,
1190
+ filterKeys,
1191
+ customizedSync: {
1192
+ withoutRedisHashmap: true,
1193
+ withoutSetSync: true
1194
+ }
1195
+ });
1196
+ const redisGet = this.client.get.bind(this.client);
1197
+ this.redisDeduplicationHandler = new DeduplicatedRequestHandler(
1198
+ redisGet,
1199
+ inMemoryCachingTime,
1200
+ this.inMemoryDeduplicationCache
1201
+ );
1202
+ this.redisGet = redisGet;
1203
+ this.deduplicatedRedisGet = this.redisDeduplicationHandler.deduplicatedFunction;
1149
1204
  } catch (error) {
1150
1205
  console.error("RedisCacheComponentsHandler constructor error", error);
1151
1206
  throw error;
@@ -1180,9 +1235,10 @@ var RedisCacheComponentsHandler = class {
1180
1235
  const redisKey = `${this.keyPrefix}${cacheKey}`;
1181
1236
  try {
1182
1237
  await this.assertClientIsReady();
1238
+ const clientGet = this.redisGetDeduplication ? this.deduplicatedRedisGet(cacheKey) : this.redisGet;
1183
1239
  const serialized = await redisErrorHandler(
1184
- "RedisCacheComponentsHandler.get(), operation: get " + this.getTimeoutMs + "ms " + redisKey,
1185
- this.client.get(
1240
+ "RedisCacheComponentsHandler.get(), operation: get" + (this.redisGetDeduplication ? " deduplicated" : "") + " " + this.getTimeoutMs + "ms " + redisKey,
1241
+ clientGet(
1186
1242
  commandOptions2({ signal: AbortSignal.timeout(this.getTimeoutMs) }),
1187
1243
  redisKey
1188
1244
  )
@@ -1255,6 +1311,9 @@ var RedisCacheComponentsHandler = class {
1255
1311
  console.error("Stored object:", stored);
1256
1312
  throw jsonError;
1257
1313
  }
1314
+ if (this.redisGetDeduplication) {
1315
+ this.redisDeduplicationHandler.seedRequestReturn(cacheKey, serialized);
1316
+ }
1258
1317
  const ttlSeconds = Number.isFinite(stored.expire) && stored.expire > 0 ? Math.floor(stored.expire) : void 0;
1259
1318
  const redisKey = `${this.keyPrefix}${cacheKey}`;
1260
1319
  const setOperation = redisErrorHandler(
@@ -1321,6 +1380,11 @@ var RedisCacheComponentsHandler = class {
1321
1380
  "RedisCacheComponentsHandler.updateTags(), operation: unlink",
1322
1381
  this.client.unlink(fullRedisKeys)
1323
1382
  );
1383
+ if (this.redisGetDeduplication && this.inMemoryCachingTime > 0) {
1384
+ for (const key of keysToDelete) {
1385
+ this.inMemoryDeduplicationCache.delete(key);
1386
+ }
1387
+ }
1324
1388
  const deleteTagsOperation = this.sharedTagsMap.delete(cacheKeys);
1325
1389
  await deleteTagsOperation;
1326
1390
  } catch (error) {