diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index e87a4f2ea9..b3a165643c 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -5,11 +5,11 @@ on: pull_request: branches: [main] schedule: - - cron: '0 3 * * *' # Run every day at 3am UTC + - cron: "0 3 * * *" # Run every day at 3am UTC workflow_dispatch: inputs: versions: - description: 'The versions of Next.js to test against (escape-quoted and comma separated)' + description: "The versions of Next.js to test against (escape-quoted and comma separated)" required: false # TODO(serhalp) Ideally this would simply accept bare quotes but we're having trouble # parsing that so this will do for now. @@ -52,7 +52,6 @@ jobs: group: ${{ steps.set-matrix.outputs.group }} total: ${{ steps.set-matrix.outputs.total }} steps: - - name: Set Next.js versions to test id: set-matrix run: | @@ -240,7 +239,7 @@ jobs: IS_WEBPACK_TEST: ${{ steps.decide-default-bundler.outputs.default_bundler == 'webpack' && '1' || '' }} IS_TURBOPACK_TEST: ${{ steps.decide-default-bundler.outputs.default_bundler == 'turbopack' && '1' || '' }} NODE_OPTIONS: --import ${{ github.workspace }}/${{ env.runtime-path}}/tools/fetch-retry.mjs - run: node run-tests.js -g ${{ matrix.group }}/${{ needs.setup.outputs.total }} -c ${TEST_CONCURRENCY} --type e2e + run: node run-tests.js -g ${{ matrix.group }}/${{ needs.setup.outputs.total }} -c ${TEST_CONCURRENCY} --type e2e --retries 5 working-directory: ${{ env.next-path }} - name: Upload Test Results @@ -250,7 +249,7 @@ jobs: name: test-result-${{matrix.version_spec.selector}}-${{ matrix.group }} path: ${{ env.next-path }}/test/${{steps.decide-default-bundler.outputs.default_bundler == 'turbopack' && 'turbopack-test-junit-report' || 'test-junit-report'}}/*.xml publish-test-results: - name: 'E2E Test Summary (${{matrix.version_spec.selector}})' + name: "E2E Test Summary (${{matrix.version_spec.selector}})" needs: - e2e - setup diff --git a/package-lock.json b/package-lock.json index 34f1073409..0349d4fc9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "netlify-cli": "23.9.2", "next": "^15.0.0-canary.28", "next-with-cache-handler-v2": "npm:next@15.3.0-canary.13", + "next-with-cache-handler-v3": "npm:next@16.0.0-beta.0", "os": "^0.1.2", "outdent": "^0.8.0", "p-limit": "^6.0.0", @@ -2852,6 +2853,17 @@ "dev": true, "license": "ISC" }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -2966,6 +2978,23 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", @@ -3080,6 +3109,29 @@ "@img/sharp-libvips-linux-arm64": "1.0.4" } }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", + "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.3" + } + }, "node_modules/@img/sharp-linux-s390x": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", @@ -3192,6 +3244,26 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz", + "integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-ia32": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", @@ -27972,6 +28044,637 @@ "tslib": "^2.8.0" } }, + "node_modules/next-with-cache-handler-v3": { + "name": "next", + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/next/-/next-16.0.0-beta.0.tgz", + "integrity": "sha512-RrpQl/FkN4v+hwcfsgj+ukTDyf3uQ1mcbNs229M9H0POMc8P0LhgrNDAWEiQHviYicLZorWJ47RoQYCzVddkww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/env": "16.0.0-beta.0", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.0.0-beta.0", + "@next/swc-darwin-x64": "16.0.0-beta.0", + "@next/swc-linux-arm64-gnu": "16.0.0-beta.0", + "@next/swc-linux-arm64-musl": "16.0.0-beta.0", + "@next/swc-linux-x64-gnu": "16.0.0-beta.0", + "@next/swc-linux-x64-musl": "16.0.0-beta.0", + "@next/swc-win32-arm64-msvc": "16.0.0-beta.0", + "@next/swc-win32-x64-msvc": "16.0.0-beta.0", + "sharp": "^0.34.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-linux-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-wasm32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.5.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@img/sharp-win32-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/env": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.0-beta.0.tgz", + "integrity": "sha512-OWeEhUmIxA9zuQansxKXHWTszsPcvSvar8ym1BOElhU6Lgnb4yLXGshKSoPXoHOHRFcxuYmhI86OA+5Z9TvSSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-darwin-arm64": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.0-beta.0.tgz", + "integrity": "sha512-8IdA5j+xOGQNP+4yBqG5pvNhrDrVp/IMyJSn38t2h3XOhw+BZ63j+m0SyJuj2OKgIBgJLvkHUXEWiSD9u5nfBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-darwin-x64": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.0-beta.0.tgz", + "integrity": "sha512-SEAPWkMPHnLMTmDw/b0XnIgMsdUZvAGLYzAz9VZxtie1x5dnus3t/n2DP0nmg8O8LkfKJcicnm6fMrNeHJQs9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.0-beta.0.tgz", + "integrity": "sha512-qeccINBs3eWt5EbiaABNUZBWyMRToZfgzSD5tRED1UuZpfxt3asebkanV1GFS/ZQ+z3+pVEzMwhaGwCBbfCa5w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-linux-arm64-musl": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.0-beta.0.tgz", + "integrity": "sha512-vhhfBp7CNTVHq0tuY+enPKvE91QgjhiWs539EQ0VXCbQMoAuxWr1uOgS3kjfah78oI89icQin4HAO7ePu3KUtw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-linux-x64-gnu": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.0-beta.0.tgz", + "integrity": "sha512-2+aMU293kgg0UJLEyhgXy3KwyI0RcSfKHrWT8SnzW8FqcrUcOWYw7qWCP+JcRT5SwQCcjByEOwH+cw+1nBTeIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-linux-x64-musl": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.0-beta.0.tgz", + "integrity": "sha512-Jgu9BvRLG82DhkeSF+3OTOkZXf6azXlOlQ3TOWHRzh+Cap+fhlO8yp+cYI5jDsopDIfaBW+3ToAL1YLE1n+dGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.0-beta.0.tgz", + "integrity": "sha512-5cGucadLwCWUl9v1aOJLzDpyiYpdrFBiApvGVy4GKAFo6uK34mtgCSZcVUQ+DeLjAx0G5B3AgNxVnzMfXKsv5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@next/swc-win32-x64-msvc": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.0-beta.0.tgz", + "integrity": "sha512-gq0WvicjqmoiakDtW7TeabgT58i+5mQ+wy+qYuwCHBbWbed9PMh/wl4ZomsOe2IzlinRPylRGA01jXLPOrX/Nw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/next-with-cache-handler-v3/node_modules/sharp": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4" + } + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -34705,6 +35408,13 @@ "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", "dev": true }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "optional": true + }, "@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -34753,6 +35463,13 @@ "dev": true, "optional": true }, + "@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "dev": true, + "optional": true + }, "@img/sharp-libvips-linux-s390x": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", @@ -34801,6 +35518,16 @@ "@img/sharp-libvips-linux-arm64": "1.0.4" } }, + "@img/sharp-linux-ppc64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", + "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linux-ppc64": "1.2.3" + } + }, "@img/sharp-linux-s390x": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", @@ -34851,6 +35578,13 @@ "@emnapi/runtime": "^1.2.0" } }, + "@img/sharp-win32-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz", + "integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==", + "dev": true, + "optional": true + }, "@img/sharp-win32-ia32": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", @@ -52216,6 +52950,295 @@ } } }, + "next-with-cache-handler-v3": { + "version": "npm:next@16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/next/-/next-16.0.0-beta.0.tgz", + "integrity": "sha512-RrpQl/FkN4v+hwcfsgj+ukTDyf3uQ1mcbNs229M9H0POMc8P0LhgrNDAWEiQHviYicLZorWJ47RoQYCzVddkww==", + "dev": true, + "requires": { + "@next/env": "16.0.0-beta.0", + "@next/swc-darwin-arm64": "16.0.0-beta.0", + "@next/swc-darwin-x64": "16.0.0-beta.0", + "@next/swc-linux-arm64-gnu": "16.0.0-beta.0", + "@next/swc-linux-arm64-musl": "16.0.0-beta.0", + "@next/swc-linux-x64-gnu": "16.0.0-beta.0", + "@next/swc-linux-x64-musl": "16.0.0-beta.0", + "@next/swc-win32-arm64-msvc": "16.0.0-beta.0", + "@next/swc-win32-x64-msvc": "16.0.0-beta.0", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "sharp": "^0.34.4", + "styled-jsx": "5.1.6" + }, + "dependencies": { + "@img/sharp-darwin-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.2.3" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.2.3" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "dev": true, + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.2.3" + } + }, + "@img/sharp-linux-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.2.3" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" + } + }, + "@img/sharp-wasm32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/runtime": "^1.5.0" + } + }, + "@img/sharp-win32-ia32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", + "dev": true, + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", + "dev": true, + "optional": true + }, + "@next/env": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.0-beta.0.tgz", + "integrity": "sha512-OWeEhUmIxA9zuQansxKXHWTszsPcvSvar8ym1BOElhU6Lgnb4yLXGshKSoPXoHOHRFcxuYmhI86OA+5Z9TvSSQ==", + "dev": true + }, + "@next/swc-darwin-arm64": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.0-beta.0.tgz", + "integrity": "sha512-8IdA5j+xOGQNP+4yBqG5pvNhrDrVp/IMyJSn38t2h3XOhw+BZ63j+m0SyJuj2OKgIBgJLvkHUXEWiSD9u5nfBw==", + "dev": true, + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.0-beta.0.tgz", + "integrity": "sha512-SEAPWkMPHnLMTmDw/b0XnIgMsdUZvAGLYzAz9VZxtie1x5dnus3t/n2DP0nmg8O8LkfKJcicnm6fMrNeHJQs9w==", + "dev": true, + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.0-beta.0.tgz", + "integrity": "sha512-qeccINBs3eWt5EbiaABNUZBWyMRToZfgzSD5tRED1UuZpfxt3asebkanV1GFS/ZQ+z3+pVEzMwhaGwCBbfCa5w==", + "dev": true, + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.0-beta.0.tgz", + "integrity": "sha512-vhhfBp7CNTVHq0tuY+enPKvE91QgjhiWs539EQ0VXCbQMoAuxWr1uOgS3kjfah78oI89icQin4HAO7ePu3KUtw==", + "dev": true, + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.0-beta.0.tgz", + "integrity": "sha512-2+aMU293kgg0UJLEyhgXy3KwyI0RcSfKHrWT8SnzW8FqcrUcOWYw7qWCP+JcRT5SwQCcjByEOwH+cw+1nBTeIA==", + "dev": true, + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.0-beta.0.tgz", + "integrity": "sha512-Jgu9BvRLG82DhkeSF+3OTOkZXf6azXlOlQ3TOWHRzh+Cap+fhlO8yp+cYI5jDsopDIfaBW+3ToAL1YLE1n+dGg==", + "dev": true, + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.0-beta.0.tgz", + "integrity": "sha512-5cGucadLwCWUl9v1aOJLzDpyiYpdrFBiApvGVy4GKAFo6uK34mtgCSZcVUQ+DeLjAx0G5B3AgNxVnzMfXKsv5g==", + "dev": true, + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "16.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.0-beta.0.tgz", + "integrity": "sha512-gq0WvicjqmoiakDtW7TeabgT58i+5mQ+wy+qYuwCHBbWbed9PMh/wl4ZomsOe2IzlinRPylRGA01jXLPOrX/Nw==", + "dev": true, + "optional": true + }, + "@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dev": true, + "requires": { + "tslib": "^2.8.0" + } + }, + "sharp": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "dev": true, + "optional": true, + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" + } + } + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/package.json b/package.json index 855f01083a..97bc7d50d4 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "netlify-cli": "23.9.2", "next": "^15.0.0-canary.28", "next-with-cache-handler-v2": "npm:next@15.3.0-canary.13", + "next-with-cache-handler-v3": "npm:next@16.0.0-beta.0", "os": "^0.1.2", "outdent": "^0.8.0", "p-limit": "^6.0.0", diff --git a/src/run/handlers/request-context.cts b/src/run/handlers/request-context.cts index 9aabb7ca0a..fd8a13139e 100644 --- a/src/run/handlers/request-context.cts +++ b/src/run/handlers/request-context.cts @@ -21,6 +21,7 @@ export type RequestContext = { serverTiming?: string routeHandlerRevalidate?: NetlifyCachedRouteValue['revalidate'] pageHandlerRevalidate?: NetlifyCachedRouteValue['revalidate'] + ongoingRevalidations?: Map> /** * Track promise running in the background and need to be waited for. * Uses `context.waitUntil` if available, otherwise stores promises to diff --git a/src/run/handlers/tags-handler.cts b/src/run/handlers/tags-handler.cts index 3f896b6722..f354ab3d58 100644 --- a/src/run/handlers/tags-handler.cts +++ b/src/run/handlers/tags-handler.cts @@ -214,10 +214,30 @@ export function markTagsAsStaleAndPurgeEdgeCache( ) { const tags = getCacheTagsFromTagOrTags(tagOrTags) + // Next.js is calling classic CacheHandler.revalidateTag and 'use cache' CacheHandler expireTags/updateTags separately + // this results in duplicate work being done (it doesn't cause problems, but it is inefficient) + // See https://github.com/vercel/next.js/blob/8cab15c0c947a71eb8606ba29da719a2e121fc88/packages/next/src/server/revalidation-utils.ts#L170-L180 + // Deduping those within context of a single request might catch unrelated invalidations, so instead of using just request context + // we will check if they happened in same event loop tick as well. + const revalidationKey = JSON.stringify({ tags, durations }) + const requestContext = getRequestContext() + + if (requestContext) { + const ongoingRevalidation = requestContext.ongoingRevalidations?.get(revalidationKey) + if (ongoingRevalidation) { + // If we already have an ongoing revalidation for this key, we can use it + return ongoingRevalidation + } + } + const revalidateTagPromise = doRevalidateTagAndPurgeEdgeCache(tags, durations) - const requestContext = getRequestContext() if (requestContext) { + requestContext.ongoingRevalidations ??= new Map() + requestContext.ongoingRevalidations.set(revalidationKey, revalidateTagPromise) + process.nextTick(() => { + requestContext.ongoingRevalidations?.delete(revalidationKey) + }) requestContext.trackBackgroundWork(revalidateTagPromise) } diff --git a/src/run/handlers/use-cache-handler.ts b/src/run/handlers/use-cache-handler.ts index 510a2087b5..0f0d7a7d4a 100644 --- a/src/run/handlers/use-cache-handler.ts +++ b/src/run/handlers/use-cache-handler.ts @@ -5,14 +5,16 @@ import type { CacheEntry, // only supporting latest variant (https://github.com/vercel/next.js/pull/76687) // first released in v15.3.0-canary.13 - CacheHandlerV2 as CacheHandler, + CacheHandlerV2, } from 'next-with-cache-handler-v2/dist/server/lib/cache-handlers/types.js' +import type { CacheHandler as CacheHandlerWithUpdateTag } from 'next-with-cache-handler-v3/dist/server/lib/cache-handlers/types.js' import { getLogger } from './request-context.cjs' import { getMostRecentTagExpirationTimestamp, isAnyTagStaleOrExpired, markTagsAsStaleAndPurgeEdgeCache, + RevalidateTagDurations, } from './tags-handler.cjs' import { getTracer } from './tracer.cjs' @@ -38,6 +40,8 @@ type PrivateCacheEntry = { type CacheHandleLRUCache = LRUCache type PendingSets = Map> +type MultiVersionCacheHandler = CacheHandlerV2 & CacheHandlerWithUpdateTag + const LRU_CACHE_GLOBAL_KEY = Symbol.for('nf-use-cache-handler-lru-cache') const PENDING_SETS_GLOBAL_KEY = Symbol.for('nf-use-cache-handler-pending-sets') const cacheHandlersSymbol = Symbol.for('@next/cache-handlers') @@ -51,8 +55,8 @@ const extendedGlobalThis = globalThis as typeof globalThis & { // Used by Next.js to provide implementation of cache handlers [cacheHandlersSymbol]?: { - RemoteCache?: CacheHandler - DefaultCache?: CacheHandler + RemoteCache?: MultiVersionCacheHandler + DefaultCache?: MultiVersionCacheHandler } } @@ -86,10 +90,10 @@ function getPendingSets(): PendingSets { const tmpResolvePendingBeforeCreatingAPromise = () => {} export const NetlifyDefaultUseCacheHandler = { - get(cacheKey: string): ReturnType { + get(cacheKey: string): ReturnType { return getTracer().withActiveSpan( 'DefaultUseCacheHandler.get', - async (span): ReturnType => { + async (span): ReturnType => { getLogger().withFields({ cacheKey }).debug(`[NetlifyDefaultUseCacheHandler] get`) span.setAttributes({ cacheKey, @@ -127,45 +131,54 @@ export const NetlifyDefaultUseCacheHandler = { return undefined } - const { stale } = await isAnyTagStaleOrExpired(entry.tags, entry.timestamp) + const { stale, expired } = await isAnyTagStaleOrExpired(entry.tags, entry.timestamp) - if (stale) { + if (expired) { getLogger() - .withFields({ cacheKey, ttl, status: 'STALE BY TAG' }) + .withFields({ cacheKey, ttl, status: 'EXPIRED BY TAG' }) .debug(`[NetlifyDefaultUseCacheHandler] get result`) span.setAttributes({ - cacheStatus: 'stale tag, discarded', + cacheStatus: 'expired tag, discarded', ttl, }) return undefined } + let { revalidate, value } = entry + if (stale) { + revalidate = -1 + } + // returning entry will cause stream to be consumed // so we need to clone it first, so in-memory cache can // be used again - const [returnStream, newSaved] = entry.value.tee() + const [returnStream, newSaved] = value.tee() entry.value = newSaved getLogger() - .withFields({ cacheKey, ttl, status: 'HIT' }) + .withFields({ cacheKey, ttl, status: stale ? 'STALE' : 'HIT' }) .debug(`[NetlifyDefaultUseCacheHandler] get result`) span.setAttributes({ - cacheStatus: 'hit', + cacheStatus: stale ? 'stale' : 'hit', ttl, }) return { ...entry, + revalidate, value: returnStream, } }, ) }, - set(cacheKey: string, pendingEntry: Promise): ReturnType { + set( + cacheKey: string, + pendingEntry: Promise, + ): ReturnType { return getTracer().withActiveSpan( 'DefaultUseCacheHandler.set', - async (span): ReturnType => { + async (span): ReturnType => { getLogger().withFields({ cacheKey }).debug(`[NetlifyDefaultUseCacheHandler]: set`) span.setAttributes({ cacheKey, @@ -223,10 +236,15 @@ export const NetlifyDefaultUseCacheHandler = { // we would need to check more tags than current request needs // while blocking pipeline }, - getExpiration: function (...tags: string[]): ReturnType { + getExpiration: function ( + // supporting both (...tags: string[]) and (tags: string[]) signatures + ...notNormalizedTags: string[] | string[][] + ): ReturnType { return getTracer().withActiveSpan( 'DefaultUseCacheHandler.getExpiration', - async (span): ReturnType => { + async (span): ReturnType => { + const tags = notNormalizedTags.flat() + span.setAttributes({ tags, }) @@ -244,10 +262,11 @@ export const NetlifyDefaultUseCacheHandler = { }, ) }, - expireTags(...tags: string[]): ReturnType { + // this is for CacheHandlerV2 + expireTags(...tags: string[]): ReturnType { return getTracer().withActiveSpan( 'DefaultUseCacheHandler.expireTags', - async (span): ReturnType => { + async (span): ReturnType => { getLogger().withFields({ tags }).debug(`[NetlifyDefaultUseCacheHandler] expireTags`) span.setAttributes({ tags, @@ -257,7 +276,26 @@ export const NetlifyDefaultUseCacheHandler = { }, ) }, -} satisfies CacheHandler + // this is for CacheHandlerV3 / Next 16 + updateTags( + tags: string[], + durations: RevalidateTagDurations, + ): ReturnType { + return getTracer().withActiveSpan( + 'DefaultUseCacheHandler.updateTags', + async (span): ReturnType => { + getLogger() + .withFields({ tags, durations }) + .debug(`[NetlifyDefaultUseCacheHandler] updateTags`) + span.setAttributes({ + tags, + durations: JSON.stringify(durations), + }) + await markTagsAsStaleAndPurgeEdgeCache(tags, durations) + }, + ) + }, +} satisfies MultiVersionCacheHandler export function configureUseCacheHandlers() { extendedGlobalThis[cacheHandlersSymbol] = { diff --git a/tests/fixtures/use-cache/app/api/revalidate/[...slug]/route.ts b/tests/fixtures/use-cache/app/api/revalidate/[...slug]/route.ts index 519d5c0267..56e22416c2 100644 --- a/tests/fixtures/use-cache/app/api/revalidate/[...slug]/route.ts +++ b/tests/fixtures/use-cache/app/api/revalidate/[...slug]/route.ts @@ -9,8 +9,12 @@ export async function GET(request: NextRequest, { params }) { const { slug } = await params const tagToInvalidate = slug.join('/') + let profile = undefined + if (request.nextUrl.searchParams.has('expire')) { + profile = { expire: parseInt(request.nextUrl.searchParams.get('expire')) } + } - revalidateTag(tagToInvalidate) + revalidateTag(tagToInvalidate, profile) return Response.json({ tagToInvalidate }) } diff --git a/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-1year/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-1year/[slug]/page.tsx index b6cb766e3e..024b115a88 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-1year/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-1year/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, getDataImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-5seconds/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-5seconds/[slug]/page.tsx index 2376bcefa5..52498d28a9 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-5seconds/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-component/dynamic/ttl-5seconds/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, getDataImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-10seconds/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-10seconds/[slug]/page.tsx index 4e79023f1d..317b652200 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-10seconds/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-10seconds/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, generateStaticParamsImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-1year/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-1year/[slug]/page.tsx index 63fe621296..7c5d306a6b 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-1year/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-component/static/ttl-1year/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, generateStaticParamsImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-1year/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-1year/[slug]/page.tsx index f07a3e05e4..8733d513ec 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-1year/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-1year/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, getDataImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-5seconds/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-5seconds/[slug]/page.tsx index 25517481fd..e00d29689d 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-5seconds/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-data/dynamic/ttl-5seconds/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, getDataImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-10seconds/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-10seconds/[slug]/page.tsx index 32374f0910..ae05c72f12 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-10seconds/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-10seconds/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, generateStaticParamsImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-1year/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-1year/[slug]/page.tsx index 4da6621b65..f83ba80ec6 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-1year/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-data/static/ttl-1year/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, generateStaticParamsImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-1year/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-1year/[slug]/page.tsx index 416e2d9ac5..4c4ef7b1a2 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-1year/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-1year/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, getDataImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-5seconds/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-5seconds/[slug]/page.tsx index a4939f2597..0a9e3c3426 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-5seconds/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-page/dynamic/ttl-5seconds/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, getDataImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-10seconds/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-10seconds/[slug]/page.tsx index bc50627f89..1e99da84fe 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-10seconds/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-10seconds/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, generateStaticParamsImplementation, diff --git a/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-1year/[slug]/page.tsx b/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-1year/[slug]/page.tsx index ed6c3e68dc..7552666d96 100644 --- a/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-1year/[slug]/page.tsx +++ b/tests/fixtures/use-cache/app/default/use-cache-page/static/ttl-1year/[slug]/page.tsx @@ -1,4 +1,4 @@ -import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache' +import { cacheLife, cacheTag } from '../../../../../next-cache' import { BasePageComponentProps, generateStaticParamsImplementation, diff --git a/tests/fixtures/use-cache/app/next-cache.ts b/tests/fixtures/use-cache/app/next-cache.ts new file mode 100644 index 0000000000..d761050c98 --- /dev/null +++ b/tests/fixtures/use-cache/app/next-cache.ts @@ -0,0 +1,20 @@ +import * as NextCacheTyped from 'next/cache' + +const NextCache = NextCacheTyped as any + +export const cacheLife: any = + 'cacheLife' in NextCache + ? NextCache.cacheLife + : 'unstable_cacheLife' in NextCache + ? NextCache.unstable_cacheLife + : () => { + throw new Error('both unstable_cacheLife and cacheLife are missing from next/cache') + } +export const cacheTag: any = + 'cacheTag' in NextCache + ? NextCache.cacheTag + : 'unstable_cacheTag' in NextCache + ? NextCache.unstable_cacheTag + : () => { + throw new Error('both unstable_cacheTag and cacheTag are missing from next/cache') + } diff --git a/tests/integration/use-cache.test.ts b/tests/integration/use-cache.test.ts index 93d22b1b90..558e165445 100644 --- a/tests/integration/use-cache.test.ts +++ b/tests/integration/use-cache.test.ts @@ -290,6 +290,54 @@ describe.skipIf(!nextVersionSatisfies('>=15.3.0-canary.13'))('use cache', () => pageComponentTimeShouldBeEqual: false, }) }) + + test.skipIf(!nextVersionSatisfies('>=16.0.0-alpha.0'))( + 'invalidating tag with SWR on one lambda result in invalidating them on all lambdas', + async () => { + const url = `${routeRoot}/different-lambdas-tag-invalidation` + + const { invokeFunction: invokeFunctionLambda1 } = await loadSandboxedFunction(ctx) + const { invokeFunction: invokeFunctionLambda2 } = await loadSandboxedFunction(ctx) + + const call1 = await invokeFunctionLambda1({ url }) + expect(call1).not.toBeCacheableResponse() + + await invokeFunctionLambda2({ + url: `/api/revalidate/${useCacheTagPrefix}/${url}?expire=5`, + }) + + const call2 = await invokeFunctionLambda1({ url }) + expect(call2).toHaveExpectedCachingBehavior( + call1, + // if we hit before 5 second expire timeframe passes, we expect to get stale response + expectedCachingBehaviorWhenUseCacheRegenerates, + ) + + const call3 = await invokeFunctionLambda1({ url }) + expect(call3).toHaveExpectedCachingBehavior(call2, { + // previous request should trigger revalidation in background and cause everything to be fresh on this request + getDataTimeShouldBeEqual: false, + resultWrapperComponentTimeShouldBeEqual: false, + pageComponentTimeShouldBeEqual: false, + }) + + await invokeFunctionLambda2({ + url: `/api/revalidate/${useCacheTagPrefix}/${url}?expire=5`, + }) + + // let's sleep for expire period to test that expire period is honored + await new Promise((resolve) => setTimeout(resolve, 6000)) + + const call4 = await invokeFunctionLambda1({ url }) + expect(call4).toHaveExpectedCachingBehavior(call3, { + // we are not getting stale response here because we issued request after expire period + // so everything should be fresh + getDataTimeShouldBeEqual: false, + resultWrapperComponentTimeShouldBeEqual: false, + pageComponentTimeShouldBeEqual: false, + }) + }, + ) }) describe('TTL=5 seconds', () => { diff --git a/tests/utils/create-e2e-fixture.ts b/tests/utils/create-e2e-fixture.ts index a03be0fe05..0af1e20a1d 100644 --- a/tests/utils/create-e2e-fixture.ts +++ b/tests/utils/create-e2e-fixture.ts @@ -542,12 +542,18 @@ export const fixtureFactories = { packagePath: 'apps/next-app', buildCommand: 'nx run next-app:build', publishDirectory: 'dist/apps/next-app/.next', + env: { + NX_ISOLATE_PLUGINS: 'false', + }, }), nxIntegratedDistDir: () => createE2EFixture('nx-integrated', { packagePath: 'apps/custom-dist-dir', buildCommand: 'nx run custom-dist-dir:build', publishDirectory: 'dist/apps/custom-dist-dir/dist', + env: { + NX_ISOLATE_PLUGINS: 'false', + }, }), cliBeforeRegionalBlobsSupport: () => createE2EFixture('cli-before-regional-blobs-support', {