@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.
- package/README.md +87 -24
- package/dist/index.d.mts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +291 -116
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +288 -115
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
- package/.cursor/skills/testing/SKILL.md +0 -121
- package/.github/workflows/ci.yml +0 -309
- package/.github/workflows/pages.yml +0 -38
- package/.github/workflows/release.yml +0 -207
- package/.prettierrc.json +0 -6
- package/.vscode/settings.json +0 -10
- package/ARCHITECTURE.md +0 -431
- package/CHANGELOG.md +0 -411
- package/commitlint.config.cjs +0 -4
- package/docker/redis.conf +0 -1
- package/docs/index.html +0 -140
- package/docs/robots.txt +0 -4
- package/docs/sitemap.xml +0 -8
- package/docs/styles.css +0 -141
- package/eslint.config.mjs +0 -26
- package/hooks/commit-msg +0 -10
- package/hooks/pre-commit +0 -11
- package/playwright.config.ts +0 -18
- package/release.config.cjs +0 -31
- package/scripts/prepare.sh +0 -7
- package/scripts/setup-git.sh +0 -8
- package/scripts/setup-repo-name.sh +0 -17
- package/scripts/vitest-run-staged.cjs +0 -44
- package/src/CacheComponentsHandler.ts +0 -567
- package/src/CachedHandler.ts +0 -53
- package/src/DeduplicatedRequestHandler.ts +0 -110
- package/src/RedisStringsHandler.ts +0 -937
- package/src/SyncedMap.ts +0 -482
- package/src/ZodHandler.ts +0 -45
- package/src/index.ts +0 -16
- package/src/serializer.ts +0 -59
- package/src/utils/debug.ts +0 -30
- package/src/utils/json.ts +0 -49
- package/src/utils/prefix.ts +0 -44
- package/test/README.md +0 -194
- package/test/nextjs-test-projects/next-app-15-4-11/README.md +0 -36
- package/test/nextjs-test-projects/next-app-15-4-11/eslint.config.mjs +0 -16
- package/test/nextjs-test-projects/next-app-15-4-11/next.config.js +0 -9
- package/test/nextjs-test-projects/next-app-15-4-11/package-lock.json +0 -5969
- package/test/nextjs-test-projects/next-app-15-4-11/package.json +0 -29
- package/test/nextjs-test-projects/next-app-15-4-11/pnpm-lock.yaml +0 -3726
- package/test/nextjs-test-projects/next-app-15-4-11/postcss.config.mjs +0 -5
- package/test/nextjs-test-projects/next-app-15-4-11/public/file.svg +0 -1
- package/test/nextjs-test-projects/next-app-15-4-11/public/globe.svg +0 -1
- package/test/nextjs-test-projects/next-app-15-4-11/public/next.svg +0 -1
- package/test/nextjs-test-projects/next-app-15-4-11/public/vercel.svg +0 -1
- package/test/nextjs-test-projects/next-app-15-4-11/public/window.svg +0 -1
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/cached-static-fetch/route.ts +0 -18
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidatePath/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidateTag/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/revalidated-fetch/route.ts +0 -17
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/api/uncached-fetch/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/globals.css +0 -26
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/layout.tsx +0 -59
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/page.tsx +0 -755
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-15-4-11/src/app/revalidation-interface.tsx +0 -267
- package/test/nextjs-test-projects/next-app-15-4-11/tsconfig.json +0 -27
- package/test/nextjs-test-projects/next-app-16-0-11/README.md +0 -36
- package/test/nextjs-test-projects/next-app-16-0-11/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-app-16-0-11/next.config.ts +0 -10
- package/test/nextjs-test-projects/next-app-16-0-11/package.json +0 -28
- package/test/nextjs-test-projects/next-app-16-0-11/pnpm-lock.yaml +0 -4163
- package/test/nextjs-test-projects/next-app-16-0-11/postcss.config.mjs +0 -7
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/cached-static-fetch/route.ts +0 -18
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidatePath/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidateTag/route.ts +0 -20
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/revalidated-fetch/route.ts +0 -17
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/api/uncached-fetch/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/favicon.ico +0 -0
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/globals.css +0 -26
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/layout.tsx +0 -59
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/page.tsx +0 -755
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/no-fetch/default-page/page.tsx +0 -55
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/revalidation-interface.tsx +0 -267
- package/test/nextjs-test-projects/next-app-16-0-11/src/app/update-tag-test/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-0-11/tsconfig.json +0 -34
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/README.md +0 -36
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/cache-handler.js +0 -3
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/next.config.ts +0 -13
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/package.json +0 -28
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/pnpm-lock.yaml +0 -4206
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/postcss.config.mjs +0 -7
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/file.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/globe.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/next.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/vercel.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/public/window.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/page.tsx +0 -90
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/favicon.ico +0 -0
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/globals.css +0 -26
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/layout.tsx +0 -57
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/page.tsx +0 -755
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/revalidation-interface.tsx +0 -267
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/src/app/update-tag-test/page.tsx +0 -22
- package/test/nextjs-test-projects/next-app-16-0-11-cache-components/tsconfig.json +0 -34
- package/test/nextjs-test-projects/next-app-16-2-6/README.md +0 -36
- package/test/nextjs-test-projects/next-app-16-2-6/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-app-16-2-6/next.config.ts +0 -7
- package/test/nextjs-test-projects/next-app-16-2-6/package.json +0 -28
- package/test/nextjs-test-projects/next-app-16-2-6/pnpm-lock.yaml +0 -4165
- package/test/nextjs-test-projects/next-app-16-2-6/postcss.config.mjs +0 -7
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/cached-static-fetch/route.ts +0 -18
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidatePath/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidateTag/route.ts +0 -20
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/revalidated-fetch/route.ts +0 -17
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/api/uncached-fetch/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/favicon.ico +0 -0
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/globals.css +0 -26
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/layout.tsx +0 -59
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/page.tsx +0 -755
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/no-fetch/default-page/page.tsx +0 -55
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/revalidation-interface.tsx +0 -267
- package/test/nextjs-test-projects/next-app-16-2-6/src/app/update-tag-test/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-2-6/tsconfig.json +0 -34
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/README.md +0 -36
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/cache-handler.js +0 -3
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/next.config.ts +0 -13
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/package.json +0 -28
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/pnpm-lock.yaml +0 -4165
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/postcss.config.mjs +0 -7
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/file.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/globe.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/next.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/vercel.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/public/window.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/page.tsx +0 -90
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/favicon.ico +0 -0
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/globals.css +0 -26
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/layout.tsx +0 -57
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/page.tsx +0 -755
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/revalidation-interface.tsx +0 -267
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/src/app/update-tag-test/page.tsx +0 -22
- package/test/nextjs-test-projects/next-app-16-2-6-cache-components/tsconfig.json +0 -34
- package/test/nextjs-test-projects/next-app-16-3-0/README.md +0 -36
- package/test/nextjs-test-projects/next-app-16-3-0/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-app-16-3-0/next.config.ts +0 -7
- package/test/nextjs-test-projects/next-app-16-3-0/package.json +0 -28
- package/test/nextjs-test-projects/next-app-16-3-0/pnpm-lock.yaml +0 -4284
- package/test/nextjs-test-projects/next-app-16-3-0/postcss.config.mjs +0 -7
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/cached-static-fetch/route.ts +0 -18
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -27
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidatePath/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidateTag/route.ts +0 -20
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/revalidated-fetch/route.ts +0 -17
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/api/uncached-fetch/route.ts +0 -15
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/favicon.ico +0 -0
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/globals.css +0 -26
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/layout.tsx +0 -59
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/page.tsx +0 -755
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -34
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/no-fetch/default-page/page.tsx +0 -55
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -35
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -19
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -32
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/revalidation-interface.tsx +0 -267
- package/test/nextjs-test-projects/next-app-16-3-0/src/app/update-tag-test/page.tsx +0 -25
- package/test/nextjs-test-projects/next-app-16-3-0/tsconfig.json +0 -34
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/README.md +0 -36
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/cache-handler.js +0 -3
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/next.config.ts +0 -13
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/package.json +0 -28
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/pnpm-lock.yaml +0 -4284
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/postcss.config.mjs +0 -7
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/file.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/globe.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/next.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/vercel.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/public/window.svg +0 -1
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-static-fetch/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-cachelife/route.ts +0 -24
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/cached-with-tag/route.ts +0 -21
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidate-tag/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/api/revalidated-fetch/route.ts +0 -19
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/cachelife-short/page.tsx +0 -110
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/page.tsx +0 -90
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -127
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -130
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/tag-invalidation/page.tsx +0 -127
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -110
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/favicon.ico +0 -0
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/globals.css +0 -26
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/layout.tsx +0 -57
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/page.tsx +0 -755
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/revalidation-interface.tsx +0 -267
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/src/app/update-tag-test/page.tsx +0 -22
- package/test/nextjs-test-projects/next-app-16-3-0-cache-components/tsconfig.json +0 -34
- package/test/nextjs-test-projects/next-app-customized/README.md +0 -36
- package/test/nextjs-test-projects/next-app-customized/customized-cache-handler.js +0 -34
- package/test/nextjs-test-projects/next-app-customized/eslint.config.mjs +0 -16
- package/test/nextjs-test-projects/next-app-customized/next.config.js +0 -6
- package/test/nextjs-test-projects/next-pages-16-2-6/README.md +0 -16
- package/test/nextjs-test-projects/next-pages-16-2-6/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-pages-16-2-6/next.config.ts +0 -7
- package/test/nextjs-test-projects/next-pages-16-2-6/package.json +0 -26
- package/test/nextjs-test-projects/next-pages-16-2-6/pnpm-lock.yaml +0 -3896
- package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/api/revalidate.ts +0 -24
- package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/index.tsx +0 -11
- package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/isr/[slug].tsx +0 -49
- package/test/nextjs-test-projects/next-pages-16-2-6/src/pages/static-forever.tsx +0 -20
- package/test/nextjs-test-projects/next-pages-16-2-6/tsconfig.json +0 -34
- package/test/nextjs-test-projects/next-pages-16-3-0/README.md +0 -16
- package/test/nextjs-test-projects/next-pages-16-3-0/eslint.config.mjs +0 -18
- package/test/nextjs-test-projects/next-pages-16-3-0/next.config.ts +0 -7
- package/test/nextjs-test-projects/next-pages-16-3-0/package.json +0 -26
- package/test/nextjs-test-projects/next-pages-16-3-0/pnpm-lock.yaml +0 -3939
- package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/api/revalidate.ts +0 -24
- package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/index.tsx +0 -11
- package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/isr/[slug].tsx +0 -49
- package/test/nextjs-test-projects/next-pages-16-3-0/src/pages/static-forever.tsx +0 -20
- package/test/nextjs-test-projects/next-pages-16-3-0/tsconfig.json +0 -34
- package/test/playwright/cache-lab.spec.ts +0 -157
- package/test/playwright/update-tag.spec.ts +0 -33
- package/test/vitest/integration/build-id-prefix.integration.test.ts +0 -102
- package/test/vitest/integration/cache-components/cache-components.integration.test.ts +0 -231
- package/test/vitest/integration/cache-components/redis-kill-reconnect.test.ts +0 -65
- package/test/vitest/integration/cache-components/redis-quit-vs-disconnect.test.ts +0 -59
- package/test/vitest/integration/cache-components/redis-subscriber-outage.test.ts +0 -100
- package/test/vitest/integration/cache-components/scripts/redis-kill-reconnect.ts +0 -243
- package/test/vitest/integration/cache-components/scripts/redis-quit-vs-disconnect.ts +0 -113
- package/test/vitest/integration/cache-components/scripts/redis-subscriber-outage.ts +0 -379
- package/test/vitest/integration/cache-components/scripts/redis-test-helpers.ts +0 -155
- package/test/vitest/integration/nextjs-cache-handler.integration.test.ts +0 -945
- package/test/vitest/integration/pages-router.integration.test.ts +0 -420
- package/test/vitest/unit/SyncedMap-reconnect-repro.test.ts +0 -154
- package/test/vitest/unit/index.test.ts +0 -173
- package/test/vitest/unit/pages-router-kinds.test.ts +0 -292
- package/test/vitest/unit/reconnect-socket-already-opened.test.ts +0 -117
- package/test/vitest/unit/serializer.test.ts +0 -182
- package/test/vitest/unit/utils/prefix.test.ts +0 -115
- package/tsconfig.json +0 -18
- package/tsup.config.ts +0 -14
- package/vite.config.ts +0 -34
- package/vitest.cache-components.config.ts +0 -16
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
-
|
|
3
|
-
// On-demand revalidation endpoint: res.revalidate(path) re-renders the page
|
|
4
|
-
// and writes the fresh entry through the cache handler, so all instances
|
|
5
|
-
// sharing the same Redis pick it up.
|
|
6
|
-
export default async function handler(
|
|
7
|
-
req: NextApiRequest,
|
|
8
|
-
res: NextApiResponse,
|
|
9
|
-
) {
|
|
10
|
-
const path = req.query.path;
|
|
11
|
-
if (typeof path !== 'string') {
|
|
12
|
-
return res
|
|
13
|
-
.status(400)
|
|
14
|
-
.json({ revalidated: false, error: 'Missing ?path= query parameter' });
|
|
15
|
-
}
|
|
16
|
-
try {
|
|
17
|
-
await res.revalidate(path);
|
|
18
|
-
return res.json({ revalidated: true, path });
|
|
19
|
-
} catch (err) {
|
|
20
|
-
return res
|
|
21
|
-
.status(500)
|
|
22
|
-
.json({ revalidated: false, path, error: String(err) });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { GetStaticPaths, GetStaticProps } from 'next';
|
|
2
|
-
|
|
3
|
-
type Props = { slug: string; timestamp: number; counter: number };
|
|
4
|
-
|
|
5
|
-
// Per-process regeneration counter. Two server instances of the same build
|
|
6
|
-
// have independent counters, while the timestamp proves cross-instance
|
|
7
|
-
// freshness after on-demand revalidation.
|
|
8
|
-
let regenerationCounter = 0;
|
|
9
|
-
|
|
10
|
-
export default function IsrPage({ slug, timestamp, counter }: Props) {
|
|
11
|
-
return (
|
|
12
|
-
<main>
|
|
13
|
-
<h1>ISR page</h1>
|
|
14
|
-
<p>Slug: {slug}</p>
|
|
15
|
-
<p>Timestamp: {timestamp}</p>
|
|
16
|
-
<p>Counter: {counter}</p>
|
|
17
|
-
</main>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const getStaticPaths: GetStaticPaths = async () => {
|
|
22
|
-
return {
|
|
23
|
-
paths: [{ params: { slug: 'prebuilt' } }],
|
|
24
|
-
fallback: 'blocking',
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// revalidate is set high (300s) so that natural ISR regeneration does not
|
|
29
|
-
// interfere with the on-demand revalidation (res.revalidate) tests.
|
|
30
|
-
export const getStaticProps: GetStaticProps<Props> = async (context) => {
|
|
31
|
-
const slug = context.params?.slug as string;
|
|
32
|
-
|
|
33
|
-
if (slug === 'not-found') {
|
|
34
|
-
return { notFound: true, revalidate: 300 };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (slug === 'redirect') {
|
|
38
|
-
return {
|
|
39
|
-
redirect: { destination: '/static-forever', permanent: false },
|
|
40
|
-
revalidate: 300,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
regenerationCounter += 1;
|
|
45
|
-
return {
|
|
46
|
-
props: { slug, timestamp: Date.now(), counter: regenerationCounter },
|
|
47
|
-
revalidate: 300,
|
|
48
|
-
};
|
|
49
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { GetStaticProps } from 'next';
|
|
2
|
-
|
|
3
|
-
type Props = { timestamp: number };
|
|
4
|
-
|
|
5
|
-
export default function StaticForever({ timestamp }: Props) {
|
|
6
|
-
return (
|
|
7
|
-
<main>
|
|
8
|
-
<h1>Static forever</h1>
|
|
9
|
-
<p>Timestamp: {timestamp}</p>
|
|
10
|
-
</main>
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// revalidate is intentionally omitted (revalidate: false): the cache entry
|
|
15
|
-
// must fall through to the defaultStaleAge based TTL in the cache handler.
|
|
16
|
-
export const getStaticProps: GetStaticProps<Props> = async () => {
|
|
17
|
-
return {
|
|
18
|
-
props: { timestamp: Date.now() },
|
|
19
|
-
};
|
|
20
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2017",
|
|
4
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
-
"allowJs": true,
|
|
6
|
-
"skipLibCheck": true,
|
|
7
|
-
"strict": true,
|
|
8
|
-
"noEmit": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"module": "esnext",
|
|
11
|
-
"moduleResolution": "bundler",
|
|
12
|
-
"resolveJsonModule": true,
|
|
13
|
-
"isolatedModules": true,
|
|
14
|
-
"jsx": "react-jsx",
|
|
15
|
-
"incremental": true,
|
|
16
|
-
"plugins": [
|
|
17
|
-
{
|
|
18
|
-
"name": "next"
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"paths": {
|
|
22
|
-
"@/*": ["./src/*"]
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"include": [
|
|
26
|
-
"next-env.d.ts",
|
|
27
|
-
"**/*.ts",
|
|
28
|
-
"**/*.tsx",
|
|
29
|
-
".next/types/**/*.ts",
|
|
30
|
-
".next/dev/types/**/*.ts",
|
|
31
|
-
"**/*.mts"
|
|
32
|
-
],
|
|
33
|
-
"exclude": ["node_modules"]
|
|
34
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { test, expect } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
// These tests assume the Cache Components Next.js app is already running on the same origin as baseURL.
|
|
4
|
-
// Recommended:
|
|
5
|
-
// PLAYWRIGHT_BASE_URL=http://localhost:3001 pnpm test:e2e
|
|
6
|
-
|
|
7
|
-
test.describe('Cache Lab (Cache Components)', () => {
|
|
8
|
-
test('use cache non-deterministic values are stable until updateTag', async ({
|
|
9
|
-
page,
|
|
10
|
-
}) => {
|
|
11
|
-
await page.goto('/cache-lab/use-cache-nondeterministic');
|
|
12
|
-
|
|
13
|
-
const random1 = page.getByTestId('random1');
|
|
14
|
-
const now = page.getByTestId('now');
|
|
15
|
-
|
|
16
|
-
await expect(random1).toBeVisible();
|
|
17
|
-
|
|
18
|
-
const beforeRandom = await random1.textContent();
|
|
19
|
-
const beforeNow = await now.textContent();
|
|
20
|
-
|
|
21
|
-
await page.reload();
|
|
22
|
-
await expect(random1).toBeVisible();
|
|
23
|
-
|
|
24
|
-
const afterReloadRandom = await random1.textContent();
|
|
25
|
-
const afterReloadNow = await now.textContent();
|
|
26
|
-
|
|
27
|
-
expect(afterReloadRandom).toBe(beforeRandom);
|
|
28
|
-
expect(afterReloadNow).toBe(beforeNow);
|
|
29
|
-
|
|
30
|
-
await page
|
|
31
|
-
.getByRole('button', { name: "updateTag('cache-lab:nondet')" })
|
|
32
|
-
.click();
|
|
33
|
-
await page.waitForLoadState('networkidle');
|
|
34
|
-
|
|
35
|
-
await expect(random1).toBeVisible();
|
|
36
|
-
|
|
37
|
-
const afterInvalidateRandom = await random1.textContent();
|
|
38
|
-
const afterInvalidateNow = await now.textContent();
|
|
39
|
-
|
|
40
|
-
expect(afterInvalidateRandom).not.toBe(beforeRandom);
|
|
41
|
-
expect(afterInvalidateNow).not.toBe(beforeNow);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('tag invalidation changes cached values (updateTag + revalidateTag)', async ({
|
|
45
|
-
page,
|
|
46
|
-
}) => {
|
|
47
|
-
await page.goto('/cache-lab/tag-invalidation');
|
|
48
|
-
|
|
49
|
-
const createdAt = page.getByTestId('createdAt');
|
|
50
|
-
const value = page.getByTestId('value');
|
|
51
|
-
|
|
52
|
-
await expect(createdAt).toBeVisible();
|
|
53
|
-
|
|
54
|
-
const beforeCreatedAt = await createdAt.textContent();
|
|
55
|
-
const beforeValue = await value.textContent();
|
|
56
|
-
|
|
57
|
-
await page.reload();
|
|
58
|
-
await expect(createdAt).toBeVisible();
|
|
59
|
-
expect(await createdAt.textContent()).toBe(beforeCreatedAt);
|
|
60
|
-
expect(await value.textContent()).toBe(beforeValue);
|
|
61
|
-
|
|
62
|
-
await page
|
|
63
|
-
.getByRole('button', { name: "updateTag('cache-lab:tag')" })
|
|
64
|
-
.click();
|
|
65
|
-
await page.waitForLoadState('networkidle');
|
|
66
|
-
|
|
67
|
-
await expect(createdAt).toBeVisible();
|
|
68
|
-
expect(await createdAt.textContent()).not.toBe(beforeCreatedAt);
|
|
69
|
-
expect(await value.textContent()).not.toBe(beforeValue);
|
|
70
|
-
|
|
71
|
-
const afterUpdateCreatedAt = await createdAt.textContent();
|
|
72
|
-
const afterUpdateValue = await value.textContent();
|
|
73
|
-
|
|
74
|
-
await page
|
|
75
|
-
.getByRole('button', { name: "revalidateTag('cache-lab:tag')" })
|
|
76
|
-
.click();
|
|
77
|
-
await page.waitForLoadState('networkidle');
|
|
78
|
-
|
|
79
|
-
await expect(createdAt).toBeVisible();
|
|
80
|
-
|
|
81
|
-
const maybeStaleCreatedAt = await createdAt.textContent();
|
|
82
|
-
const maybeStaleValue = await value.textContent();
|
|
83
|
-
|
|
84
|
-
// Revalidate can be SWR; allow either immediate refresh or stale served,
|
|
85
|
-
// but the value should eventually change on subsequent reloads.
|
|
86
|
-
if (maybeStaleCreatedAt === afterUpdateCreatedAt) {
|
|
87
|
-
// give background refresh some time
|
|
88
|
-
await page.waitForTimeout(4000);
|
|
89
|
-
await page.reload();
|
|
90
|
-
await expect(createdAt).toBeVisible();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
expect(await createdAt.textContent()).not.toBe(afterUpdateCreatedAt);
|
|
94
|
-
expect(await value.textContent()).not.toBe(afterUpdateValue);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test('runtime cookie changes cache key (cached payload differs per cookie)', async ({
|
|
98
|
-
page,
|
|
99
|
-
}) => {
|
|
100
|
-
await page.goto('/cache-lab/runtime-data-suspense');
|
|
101
|
-
|
|
102
|
-
const cookieValue = page.getByTestId('cookie');
|
|
103
|
-
const payload = page.getByTestId('payload');
|
|
104
|
-
|
|
105
|
-
await expect(cookieValue).toBeVisible();
|
|
106
|
-
|
|
107
|
-
await page.getByRole('button', { name: 'Set cookie: user-a' }).click();
|
|
108
|
-
await page.waitForLoadState('networkidle');
|
|
109
|
-
|
|
110
|
-
await expect(cookieValue).toHaveText('user-a');
|
|
111
|
-
const payloadA1 = await payload.textContent();
|
|
112
|
-
|
|
113
|
-
await page.reload();
|
|
114
|
-
await expect(cookieValue).toHaveText('user-a');
|
|
115
|
-
const payloadA2 = await payload.textContent();
|
|
116
|
-
expect(payloadA2).toBe(payloadA1);
|
|
117
|
-
|
|
118
|
-
await page.getByRole('button', { name: 'Set cookie: user-b' }).click();
|
|
119
|
-
await page.waitForLoadState('networkidle');
|
|
120
|
-
|
|
121
|
-
await expect(cookieValue).toHaveText('user-b');
|
|
122
|
-
const payloadB = await payload.textContent();
|
|
123
|
-
|
|
124
|
-
expect(payloadB).not.toBe(payloadA1);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test('stale-while-revalidate demo eventually produces a new value after revalidateTag', async ({
|
|
128
|
-
page,
|
|
129
|
-
}) => {
|
|
130
|
-
await page.goto('/cache-lab/stale-while-revalidate');
|
|
131
|
-
|
|
132
|
-
const computedAt = page.getByTestId('computedAt');
|
|
133
|
-
const value = page.getByTestId('value');
|
|
134
|
-
|
|
135
|
-
await expect(computedAt).toBeVisible();
|
|
136
|
-
|
|
137
|
-
const beforeComputedAt = await computedAt.textContent();
|
|
138
|
-
const beforeValue = await value.textContent();
|
|
139
|
-
|
|
140
|
-
// Ensure it becomes stale.
|
|
141
|
-
await page.waitForTimeout(3000);
|
|
142
|
-
|
|
143
|
-
await page
|
|
144
|
-
.getByRole('button', { name: "revalidateTag('cache-lab:swr')" })
|
|
145
|
-
.click();
|
|
146
|
-
await page.waitForLoadState('networkidle');
|
|
147
|
-
|
|
148
|
-
// Allow some time for background refresh to potentially complete.
|
|
149
|
-
await page.waitForTimeout(4500);
|
|
150
|
-
|
|
151
|
-
await page.reload();
|
|
152
|
-
await expect(computedAt).toBeVisible();
|
|
153
|
-
|
|
154
|
-
expect(await computedAt.textContent()).not.toBe(beforeComputedAt);
|
|
155
|
-
expect(await value.textContent()).not.toBe(beforeValue);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { test, expect } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
// This test assumes the Next 16 app is already running, e.g.:
|
|
4
|
-
// cd test/nextjs-test-projects/next-app-16-0-11 && pnpm dev
|
|
5
|
-
// on the same origin as baseURL.
|
|
6
|
-
|
|
7
|
-
test('button click triggers Server Action using updateTag', async ({
|
|
8
|
-
page,
|
|
9
|
-
}) => {
|
|
10
|
-
await page.goto('/update-tag-test');
|
|
11
|
-
|
|
12
|
-
await expect(page.getByText('UpdateTag Test')).toBeVisible();
|
|
13
|
-
|
|
14
|
-
const clicks = page.getByTestId('clicks');
|
|
15
|
-
const before = await clicks.textContent();
|
|
16
|
-
|
|
17
|
-
await page.getByRole('button', { name: 'Increment' }).click();
|
|
18
|
-
|
|
19
|
-
// Wait for network and any navigation caused by the Server Action
|
|
20
|
-
await page.waitForLoadState('networkidle');
|
|
21
|
-
|
|
22
|
-
const after = await clicks.textContent();
|
|
23
|
-
|
|
24
|
-
// Server Action should have run and updated the UI
|
|
25
|
-
expect(after).not.toBe(before);
|
|
26
|
-
|
|
27
|
-
// Ensure there is no error message about updateTag usage
|
|
28
|
-
const errorLocator = page.getByText(
|
|
29
|
-
'updateTag can only be called from within a Server Action',
|
|
30
|
-
{ exact: false },
|
|
31
|
-
);
|
|
32
|
-
await expect(errorLocator).toHaveCount(0);
|
|
33
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
|
|
3
|
-
import { createClient, RedisClientType } from 'redis';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import fetch from 'node-fetch';
|
|
7
|
-
|
|
8
|
-
const NEXT_APP = 'next-app-15-4-11';
|
|
9
|
-
const PORT = 3075;
|
|
10
|
-
const BASE_URL = `http://localhost:${PORT}`;
|
|
11
|
-
|
|
12
|
-
async function waitForServer(url: string, timeout = 20000) {
|
|
13
|
-
const start = Date.now();
|
|
14
|
-
while (Date.now() - start < timeout) {
|
|
15
|
-
try {
|
|
16
|
-
const res = await fetch(url + '/api/cached-static-fetch');
|
|
17
|
-
if (res.ok) return;
|
|
18
|
-
} catch {}
|
|
19
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
20
|
-
}
|
|
21
|
-
throw new Error('Next.js server did not start in time');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('BUILD_ID-based key prefix (Next handlers)', () => {
|
|
25
|
-
let nextProcess: ChildProcessWithoutNullStreams | undefined;
|
|
26
|
-
let redis: RedisClientType;
|
|
27
|
-
let appDir: string;
|
|
28
|
-
let buildId: string;
|
|
29
|
-
|
|
30
|
-
beforeAll(async () => {
|
|
31
|
-
appDir = path.join(__dirname, '..', '..', 'nextjs-test-projects', NEXT_APP);
|
|
32
|
-
|
|
33
|
-
// Ensure clean env so BUILD_ID precedence kicks in
|
|
34
|
-
delete (process.env as Record<string, string | undefined>).KEY_PREFIX;
|
|
35
|
-
delete (process.env as Record<string, string | undefined>).VERCEL_URL;
|
|
36
|
-
process.env.VERCEL_ENV = 'production';
|
|
37
|
-
process.env.REDISHOST = process.env.REDISHOST || 'localhost';
|
|
38
|
-
process.env.REDISPORT = process.env.REDISPORT || '6379';
|
|
39
|
-
|
|
40
|
-
// Install + build
|
|
41
|
-
await new Promise<void>((resolve, reject) => {
|
|
42
|
-
const p = spawn('pnpm', ['install'], { cwd: appDir, stdio: 'inherit' });
|
|
43
|
-
p.on('close', (code) =>
|
|
44
|
-
code === 0 ? resolve() : reject(new Error('pnpm i failed')),
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
await new Promise<void>((resolve, reject) => {
|
|
48
|
-
const p = spawn('pnpm', ['build'], { cwd: appDir, stdio: 'inherit' });
|
|
49
|
-
p.on('close', (code) =>
|
|
50
|
-
code === 0 ? resolve() : reject(new Error('pnpm build failed')),
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Read BUILD_ID from the built app
|
|
55
|
-
buildId = fs
|
|
56
|
-
.readFileSync(path.join(appDir, '.next', 'BUILD_ID'), 'utf8')
|
|
57
|
-
.trim();
|
|
58
|
-
|
|
59
|
-
// Start server without KEY_PREFIX/VERCEL_URL
|
|
60
|
-
nextProcess = spawn('npx', ['next', 'start', '-p', String(PORT)], {
|
|
61
|
-
cwd: appDir,
|
|
62
|
-
env: { ...process.env, SKIP_KEYSPACE_CONFIG_CHECK: 'true' },
|
|
63
|
-
stdio: 'pipe',
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
nextProcess.stderr?.on(
|
|
67
|
-
'data',
|
|
68
|
-
(d) => process.env.DEBUG_INTEGRATION && console.error(String(d)),
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
await waitForServer(BASE_URL);
|
|
72
|
-
|
|
73
|
-
// Connect Redis
|
|
74
|
-
redis = createClient({
|
|
75
|
-
url: `redis://${process.env.REDISHOST}:${process.env.REDISPORT}`,
|
|
76
|
-
});
|
|
77
|
-
await redis.connect();
|
|
78
|
-
}, 180000);
|
|
79
|
-
|
|
80
|
-
afterAll(async () => {
|
|
81
|
-
try {
|
|
82
|
-
if (redis) await redis.quit();
|
|
83
|
-
} finally {
|
|
84
|
-
if (nextProcess) nextProcess.kill();
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('uses BUILD_ID as the Redis key prefix when no KEY_PREFIX/VERCEL_URL set', async () => {
|
|
89
|
-
// Warm cache twice to ensure a set occurs even if first was during startup
|
|
90
|
-
const res1 = await fetch(BASE_URL + '/api/cached-static-fetch');
|
|
91
|
-
expect(res1.ok).toBe(true);
|
|
92
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
93
|
-
const res2 = await fetch(BASE_URL + '/api/cached-static-fetch');
|
|
94
|
-
expect(res2.ok).toBe(true);
|
|
95
|
-
|
|
96
|
-
// Allow background syncs
|
|
97
|
-
await new Promise((r) => setTimeout(r, 1500));
|
|
98
|
-
|
|
99
|
-
const keys = await redis.keys(`${buildId}*`);
|
|
100
|
-
expect(keys.length).toBeGreaterThan(0);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import { spawn, ChildProcess } from 'child_process';
|
|
3
|
-
import { createClient, RedisClientType } from 'redis';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
|
|
6
|
-
const PORT = Number(process.env.CACHE_COMPONENTS_PORT || '3065');
|
|
7
|
-
const BASE_URL = `http://localhost:${PORT}`;
|
|
8
|
-
|
|
9
|
-
describe('Next.js 16 Cache Components Integration', () => {
|
|
10
|
-
let nextProcess: ChildProcess;
|
|
11
|
-
let redisClient: RedisClientType;
|
|
12
|
-
let keyPrefix: string;
|
|
13
|
-
|
|
14
|
-
async function delay(ms: number) {
|
|
15
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
beforeAll(async () => {
|
|
19
|
-
// Connect to Redis
|
|
20
|
-
redisClient = createClient({
|
|
21
|
-
url: process.env.REDIS_URL || 'redis://localhost:6379',
|
|
22
|
-
database: 1,
|
|
23
|
-
});
|
|
24
|
-
await redisClient.connect();
|
|
25
|
-
|
|
26
|
-
// Generate unique key prefix for this test run
|
|
27
|
-
keyPrefix = `cache-components-test-${Math.random().toString(36).substring(7)}`;
|
|
28
|
-
process.env.VERCEL_URL = keyPrefix;
|
|
29
|
-
|
|
30
|
-
const cacheComponentsApp =
|
|
31
|
-
process.env.CACHE_COMPONENTS_APP || 'next-app-16-2-6-cache-components';
|
|
32
|
-
|
|
33
|
-
const appDir = path.join(
|
|
34
|
-
__dirname,
|
|
35
|
-
'..',
|
|
36
|
-
'..',
|
|
37
|
-
'..',
|
|
38
|
-
'nextjs-test-projects',
|
|
39
|
-
cacheComponentsApp,
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
console.log('Installing Next.js app dependencies...');
|
|
43
|
-
await new Promise<void>((resolve, reject) => {
|
|
44
|
-
const installProcess = spawn('pnpm', ['install'], {
|
|
45
|
-
cwd: appDir,
|
|
46
|
-
stdio: 'inherit',
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
installProcess.on('close', (code) => {
|
|
50
|
-
if (code === 0) resolve();
|
|
51
|
-
else reject(new Error(`Install failed with code ${code}`));
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
console.log('Building Next.js app...');
|
|
56
|
-
await new Promise<void>((resolve, reject) => {
|
|
57
|
-
const buildProcess = spawn('pnpm', ['build'], {
|
|
58
|
-
cwd: appDir,
|
|
59
|
-
stdio: 'inherit',
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
buildProcess.on('close', (code) => {
|
|
63
|
-
if (code === 0) resolve();
|
|
64
|
-
else reject(new Error(`Build failed with code ${code}`));
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
console.log('Starting Next.js app...');
|
|
69
|
-
nextProcess = spawn('pnpm', ['start', '-p', PORT.toString()], {
|
|
70
|
-
cwd: appDir,
|
|
71
|
-
env: { ...process.env, VERCEL_URL: keyPrefix },
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Wait for server to be ready
|
|
75
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
76
|
-
}, 120000);
|
|
77
|
-
|
|
78
|
-
afterAll(async () => {
|
|
79
|
-
// Clean up Redis keys
|
|
80
|
-
const keys = await redisClient.keys(`${keyPrefix}*`);
|
|
81
|
-
if (keys.length > 0) {
|
|
82
|
-
await redisClient.del(keys);
|
|
83
|
-
}
|
|
84
|
-
await redisClient.quit();
|
|
85
|
-
|
|
86
|
-
// Kill Next.js process
|
|
87
|
-
if (nextProcess) {
|
|
88
|
-
nextProcess.kill();
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe('Basic use cache functionality', () => {
|
|
93
|
-
it('should cache data and return same counter value on subsequent requests', async () => {
|
|
94
|
-
// First request
|
|
95
|
-
const res1 = await fetch(`${BASE_URL}/api/cached-static-fetch`);
|
|
96
|
-
const data1 = await res1.json();
|
|
97
|
-
|
|
98
|
-
expect(data1.counter).toBe(1);
|
|
99
|
-
|
|
100
|
-
// Second request should return cached data
|
|
101
|
-
const res2 = await fetch(`${BASE_URL}/api/cached-static-fetch`);
|
|
102
|
-
const data2 = await res2.json();
|
|
103
|
-
|
|
104
|
-
expect(data2.counter).toBe(1); // Same counter value
|
|
105
|
-
expect(data2.timestamp).toBe(data1.timestamp); // Same timestamp
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should store cache entry in Redis', async () => {
|
|
109
|
-
await fetch(`${BASE_URL}/api/cached-static-fetch`);
|
|
110
|
-
|
|
111
|
-
// Check Redis for cache keys
|
|
112
|
-
const keys = await redisClient.keys(`${keyPrefix}*`);
|
|
113
|
-
expect(keys.length).toBeGreaterThan(0);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('cacheTag functionality', () => {
|
|
118
|
-
it('should cache data with tags', async () => {
|
|
119
|
-
const res1 = await fetch(`${BASE_URL}/api/cached-with-tag`);
|
|
120
|
-
const data1 = await res1.json();
|
|
121
|
-
|
|
122
|
-
expect(data1.counter).toBeDefined();
|
|
123
|
-
|
|
124
|
-
// Second request should return cached data
|
|
125
|
-
const res2 = await fetch(`${BASE_URL}/api/cached-with-tag`);
|
|
126
|
-
const data2 = await res2.json();
|
|
127
|
-
|
|
128
|
-
expect(data2.counter).toBe(data1.counter);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should invalidate cache when tag is revalidated (Stale while revalidate)', async () => {
|
|
132
|
-
// Get initial cached data
|
|
133
|
-
const res1 = await fetch(`${BASE_URL}/api/cached-with-tag`);
|
|
134
|
-
const data1 = await res1.json();
|
|
135
|
-
|
|
136
|
-
// Revalidate the tag
|
|
137
|
-
await fetch(`${BASE_URL}/api/revalidate-tag`, {
|
|
138
|
-
method: 'POST',
|
|
139
|
-
headers: { 'Content-Type': 'application/json' },
|
|
140
|
-
body: JSON.stringify({ tag: 'test-tag' }),
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// The cache should be invalidated - verify by making multiple requests
|
|
144
|
-
// until we get fresh data (with retries for async revalidation)
|
|
145
|
-
let freshDataReceived = false;
|
|
146
|
-
// Next.js tag revalidation can be async and may take longer under some runtimes.
|
|
147
|
-
// Use a more tolerant window to avoid flaky failures.
|
|
148
|
-
for (let i = 0; i < 60; i++) {
|
|
149
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
150
|
-
const res = await fetch(`${BASE_URL}/api/cached-with-tag`);
|
|
151
|
-
const data = await res.json();
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
data.counter !== data1.counter ||
|
|
155
|
-
data.timestamp !== data1.timestamp
|
|
156
|
-
) {
|
|
157
|
-
freshDataReceived = true;
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
expect(freshDataReceived).toBe(true);
|
|
163
|
-
}, 20_000);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('cacheLife functionality', () => {
|
|
167
|
-
it('should respect expire window and eventually return refreshed data', async () => {
|
|
168
|
-
const res1 = await fetch(`${BASE_URL}/api/cached-with-cachelife`);
|
|
169
|
-
const data1 = await res1.json();
|
|
170
|
-
expect(data1.counter).toBe(1);
|
|
171
|
-
|
|
172
|
-
const res2 = await fetch(`${BASE_URL}/api/cached-with-cachelife`);
|
|
173
|
-
const data2 = await res2.json();
|
|
174
|
-
expect(data2.counter).toBe(data1.counter);
|
|
175
|
-
expect(data2.timestamp).toBe(data1.timestamp);
|
|
176
|
-
|
|
177
|
-
await delay(6500);
|
|
178
|
-
|
|
179
|
-
let refreshedData: any;
|
|
180
|
-
for (let i = 0; i < 10; i++) {
|
|
181
|
-
const res = await fetch(`${BASE_URL}/api/cached-with-cachelife`);
|
|
182
|
-
const data = await res.json();
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
data.counter !== data1.counter ||
|
|
186
|
-
data.timestamp !== data1.timestamp
|
|
187
|
-
) {
|
|
188
|
-
refreshedData = data;
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
await delay(500);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
expect(refreshedData).toBeDefined();
|
|
196
|
-
expect(refreshedData.counter).toBeGreaterThan(data1.counter);
|
|
197
|
-
expect(refreshedData.timestamp).not.toBe(data1.timestamp);
|
|
198
|
-
}, 20_000);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe('Redis cache handler integration', () => {
|
|
202
|
-
it('should call cache handler get and set methods', async () => {
|
|
203
|
-
// Make request to trigger cache (don't clear first)
|
|
204
|
-
await fetch(`${BASE_URL}/api/cached-static-fetch`);
|
|
205
|
-
|
|
206
|
-
// Verify Redis has the cached data
|
|
207
|
-
const redisKeys = await redisClient.keys(`${keyPrefix}*`);
|
|
208
|
-
expect(redisKeys.length).toBeGreaterThan(0);
|
|
209
|
-
|
|
210
|
-
// Filter out hash keys (sharedTagsMap) and only check string keys (cache entries)
|
|
211
|
-
// Try to get each key and verify at least one is a string value
|
|
212
|
-
let foundStringKey = false;
|
|
213
|
-
for (const key of redisKeys) {
|
|
214
|
-
try {
|
|
215
|
-
const type = await redisClient.type(key);
|
|
216
|
-
if (type === 'string') {
|
|
217
|
-
const cachedValue = await redisClient.get(key);
|
|
218
|
-
if (cachedValue) {
|
|
219
|
-
foundStringKey = true;
|
|
220
|
-
expect(cachedValue).toBeTruthy();
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
} catch (e) {
|
|
225
|
-
// Skip non-string keys
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
expect(foundStringKey).toBe(true);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
});
|