From ee3effcf813bd9d0addfcd26ce241dc38550ee69 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Tue, 11 Aug 2026 23:30:30 +0530 Subject: [PATCH 01/18] add skew-protection-unplugin --- .github/workflows/release-please.yaml | 9 + .release-please-manifest.json | 1 + README.md | 13 +- package-lock.json | 1811 +++++++++++++---- package.json | 1 + packages/skew-protection-unplugin/.gitignore | 2 + packages/skew-protection-unplugin/README.md | 108 + .../skew-protection-unplugin/package.json | 79 + .../src/lib/manifest.test.ts | 63 + .../src/lib/manifest.ts | 34 + .../src/lib/options.test.ts | 92 + .../src/lib/options.ts | 70 + .../src/lib/patterns.test.ts | 32 + .../src/lib/patterns.ts | 22 + .../src/lib/test-utils.ts | 7 + .../src/lib/vite-rollup.test.ts | 154 ++ .../src/lib/vite-rollup.ts | 70 + .../src/lib/webpack.test.ts | 213 ++ .../src/lib/webpack.ts | 116 ++ .../skew-protection-unplugin/src/main.test.ts | 79 + packages/skew-protection-unplugin/src/main.ts | 38 + .../src/rollup.test.ts | 16 + .../skew-protection-unplugin/src/rollup.ts | 5 + .../skew-protection-unplugin/src/vite.test.ts | 16 + packages/skew-protection-unplugin/src/vite.ts | 5 + .../src/webpack.test.ts | 13 + .../skew-protection-unplugin/src/webpack.ts | 5 + .../skew-protection-unplugin/tsconfig.json | 17 + .../skew-protection-unplugin/tsup.config.ts | 18 + .../skew-protection-unplugin/vitest.config.ts | 9 + release-please-config.json | 1 + 31 files changed, 2747 insertions(+), 372 deletions(-) create mode 100644 packages/skew-protection-unplugin/.gitignore create mode 100644 packages/skew-protection-unplugin/README.md create mode 100644 packages/skew-protection-unplugin/package.json create mode 100644 packages/skew-protection-unplugin/src/lib/manifest.test.ts create mode 100644 packages/skew-protection-unplugin/src/lib/manifest.ts create mode 100644 packages/skew-protection-unplugin/src/lib/options.test.ts create mode 100644 packages/skew-protection-unplugin/src/lib/options.ts create mode 100644 packages/skew-protection-unplugin/src/lib/patterns.test.ts create mode 100644 packages/skew-protection-unplugin/src/lib/patterns.ts create mode 100644 packages/skew-protection-unplugin/src/lib/test-utils.ts create mode 100644 packages/skew-protection-unplugin/src/lib/vite-rollup.test.ts create mode 100644 packages/skew-protection-unplugin/src/lib/vite-rollup.ts create mode 100644 packages/skew-protection-unplugin/src/lib/webpack.test.ts create mode 100644 packages/skew-protection-unplugin/src/lib/webpack.ts create mode 100644 packages/skew-protection-unplugin/src/main.test.ts create mode 100644 packages/skew-protection-unplugin/src/main.ts create mode 100644 packages/skew-protection-unplugin/src/rollup.test.ts create mode 100644 packages/skew-protection-unplugin/src/rollup.ts create mode 100644 packages/skew-protection-unplugin/src/vite.test.ts create mode 100644 packages/skew-protection-unplugin/src/vite.ts create mode 100644 packages/skew-protection-unplugin/src/webpack.test.ts create mode 100644 packages/skew-protection-unplugin/src/webpack.ts create mode 100644 packages/skew-protection-unplugin/tsconfig.json create mode 100644 packages/skew-protection-unplugin/tsup.config.ts create mode 100644 packages/skew-protection-unplugin/vitest.config.ts diff --git a/.github/workflows/release-please.yaml b/.github/workflows/release-please.yaml index 1ba8cbbc..f637c61b 100644 --- a/.github/workflows/release-please.yaml +++ b/.github/workflows/release-please.yaml @@ -72,6 +72,15 @@ jobs: else npm publish packages/nuxt-module/ --provenance --access=public fi + - if: + ${{ steps.release.outputs['packages/skew-protection-unplugin--release_created'] || github.event_name == + 'workflow_dispatch' }} + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + npm publish packages/skew-protection-unplugin/ --provenance --access=public || true + else + npm publish packages/skew-protection-unplugin/ --provenance --access=public + fi - if: ${{ steps.release.outputs['packages/vite-plugin--release_created'] || github.event_name == 'workflow_dispatch' }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a64d3330..b9833e38 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,6 +1,7 @@ { "packages/angular-runtime": "4.0.0", "packages/nuxt-module": "0.3.9", + "packages/skew-protection-unplugin": "0.1.0", "packages/vite-plugin": "2.12.9", "packages/vite-plugin-tanstack-start": "1.3.17" } diff --git a/README.md b/README.md index 1ef4e62e..de31cc9d 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,10 @@ npm run dev ## Packages -| Name | Description | Version | -| ----------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| 🅰️ [@netlify/angular-runtime](packages/angular-runtime) | Netlify Angular Runtime - run Angular seamlessly on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/angular-runtime.svg)](https://www.npmjs.com/package/@netlify/angular-runtime) | -| 🚀 [@netlify/nuxt](packages/nuxt-module) | Nuxt module with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/nuxt.svg)](https://www.npmjs.com/package/@netlify/nuxt) | -| 🔌 [@netlify/vite-plugin](packages/vite-plugin) | Vite plugin with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin.svg)](https://www.npmjs.com/package/@netlify/vite-plugin) | -| 🔌 [@netlify/vite-plugin-tanstack-start](packages/vite-plugin-tanstack-start) | Vite plugin for TanStack Start on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin.svg)](https://www.npmjs.com/package/@netlify/vite-plugin-tanstack-start) | +| Name | Description | Version | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| 🅰️ [@netlify/angular-runtime](packages/angular-runtime) | Netlify Angular Runtime - run Angular seamlessly on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/angular-runtime.svg)](https://www.npmjs.com/package/@netlify/angular-runtime) | +| 🚀 [@netlify/nuxt](packages/nuxt-module) | Nuxt module with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/nuxt.svg)](https://www.npmjs.com/package/@netlify/nuxt) | +| 🔌 [@netlify/skew-protection-unplugin](packages/skew-protection-unplugin) | Bundler-agnostic plugin for skew-protection on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/skew-protection-unplugin.svg)](https://www.npmjs.com/package/@netlify/skew-protection-unplugin) | +| 🔌 [@netlify/vite-plugin](packages/vite-plugin) | Vite plugin with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin.svg)](https://www.npmjs.com/package/@netlify/vite-plugin) | +| 🔌 [@netlify/vite-plugin-tanstack-start](packages/vite-plugin-tanstack-start) | Vite plugin for TanStack Start on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin.svg)](https://www.npmjs.com/package/@netlify/vite-plugin-tanstack-start) | diff --git a/package-lock.json b/package-lock.json index ce3b5af1..12dcbc85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "workspaces": [ "packages/angular-runtime", "packages/nuxt-module", + "packages/skew-protection-unplugin", "packages/vite-plugin", "packages/vite-plugin-tanstack-start" ], @@ -2373,7 +2374,7 @@ "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2464,6 +2465,25 @@ } } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", @@ -4243,6 +4263,10 @@ "node": ">=18.0.0" } }, + "node_modules/@netlify/skew-protection-unplugin": { + "resolved": "packages/skew-protection-unplugin", + "link": true + }, "node_modules/@netlify/static": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/@netlify/static/-/static-3.1.10.tgz", @@ -8319,13 +8343,12 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", + "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8336,13 +8359,12 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8353,13 +8375,12 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8370,13 +8391,12 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", + "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8387,13 +8407,12 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", + "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8404,13 +8423,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8421,13 +8442,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8438,13 +8461,15 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", + "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", "cpu": [ "ppc64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8455,13 +8480,15 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", + "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", "cpu": [ "s390x" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8472,13 +8499,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", "cpu": [ "x64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8489,13 +8518,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", "cpu": [ "x64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8506,13 +8537,12 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", + "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8522,33 +8552,13 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8559,13 +8569,12 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8579,7 +8588,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@rollup/plugin-alias": { @@ -8806,9 +8815,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", - "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", "cpu": [ "arm" ], @@ -8819,9 +8828,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", - "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", "cpu": [ "arm64" ], @@ -8832,9 +8841,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", - "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", "cpu": [ "arm64" ], @@ -8845,9 +8854,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", - "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", "cpu": [ "x64" ], @@ -8858,9 +8867,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", - "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", "cpu": [ "arm64" ], @@ -8871,9 +8880,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", - "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", "cpu": [ "x64" ], @@ -8884,12 +8893,15 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", - "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8897,12 +8909,15 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", - "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", "cpu": [ "arm" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8910,12 +8925,15 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", - "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8923,12 +8941,15 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", - "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8936,12 +8957,15 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", - "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", "cpu": [ "loong64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8949,12 +8973,15 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", - "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", "cpu": [ "loong64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8962,12 +8989,15 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", - "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -8975,12 +9005,15 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", - "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", "cpu": [ "ppc64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -8988,12 +9021,15 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", - "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -9001,12 +9037,15 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", - "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", "cpu": [ "riscv64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -9014,12 +9053,15 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", - "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -9027,12 +9069,15 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", - "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -9040,12 +9085,15 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", - "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -9053,9 +9101,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", - "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", "cpu": [ "x64" ], @@ -9066,9 +9114,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", - "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", "cpu": [ "arm64" ], @@ -9079,9 +9127,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", - "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", "cpu": [ "arm64" ], @@ -9092,9 +9140,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", - "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", "cpu": [ "ia32" ], @@ -9105,9 +9153,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", - "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", "cpu": [ "x64" ], @@ -9118,9 +9166,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", - "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", "cpu": [ "x64" ], @@ -9343,6 +9391,13 @@ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/http-proxy": { "version": "1.17.17", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", @@ -9364,7 +9419,7 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/node": { @@ -10078,16 +10133,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -10096,13 +10151,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -10123,9 +10178,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10136,13 +10191,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -10150,14 +10205,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -10166,9 +10221,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -10176,13 +10231,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -10513,6 +10568,167 @@ "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==", "license": "MIT" }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, "node_modules/@whatwg-node/disposablestack": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz", @@ -10588,6 +10804,20 @@ "node": ">=18.0.0" } }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "devOptional": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "devOptional": true, + "license": "Apache-2.0" + }, "node_modules/abbrev": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", @@ -10726,6 +10956,19 @@ } } }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, "node_modules/alien-signals": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", @@ -11365,7 +11608,7 @@ "version": "2.10.36", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.36.tgz", "integrity": "sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -11678,7 +11921,7 @@ "version": "4.28.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -11949,6 +12192,17 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -11966,7 +12220,7 @@ "version": "1.0.30001799", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -12043,6 +12297,16 @@ "node": ">=18" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, "node_modules/ci-info": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", @@ -12071,6 +12335,29 @@ "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "license": "MIT" }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/clean-deep": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/clean-deep/-/clean-deep-3.4.0.tgz", @@ -13443,6 +13730,16 @@ "node": ">=0.3.1" } }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "utila": "~0.4" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -13510,6 +13807,17 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dot-prop": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", @@ -13584,7 +13892,7 @@ "version": "1.5.371", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.371.tgz", "integrity": "sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/emoji-regex": { @@ -13628,10 +13936,10 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz", - "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==", - "dev": true, + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "devOptional": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -14835,7 +15143,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -16300,6 +16608,16 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hookable": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", @@ -16355,15 +16673,156 @@ ], "license": "MIT" }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dev": true, "license": "MIT", "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.8", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.8.tgz", + "integrity": "sha512-MZmKQcTnhEh1SPSyMiEytIeDZDUoBZVorNHivQGXMASHf/BSGGOrKa2xQ5bGx3TCe1n109ecCt+cpww7wwWhKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x || 2.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" @@ -17796,6 +18255,37 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -18268,10 +18758,10 @@ "license": "MIT" }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "devOptional": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -18284,27 +18774,26 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -18319,13 +18808,12 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -18340,13 +18828,12 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -18361,13 +18848,12 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -18382,13 +18868,12 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -18403,13 +18888,15 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -18424,13 +18911,15 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -18445,13 +18934,15 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -18466,13 +18957,15 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -18487,13 +18980,12 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -18508,13 +19000,12 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -18930,6 +19421,16 @@ "node": ">= 12.0.0" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -19342,6 +19843,67 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimizer-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -20168,6 +20730,13 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/netlify-cli": { "version": "27.0.0", "resolved": "https://registry.npmjs.org/netlify-cli/-/netlify-cli-27.0.0.tgz", @@ -21634,6 +22203,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -21741,7 +22321,7 @@ "version": "2.0.47", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=18" @@ -23131,6 +23711,17 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -23243,6 +23834,17 @@ "node": ">= 0.8" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -23460,9 +24062,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { "node": ">=12" @@ -23600,9 +24202,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "funding": [ { "type": "opencollective", @@ -23619,7 +24221,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -24175,9 +24777,9 @@ } }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -24334,6 +24936,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, "node_modules/pretty-ms": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", @@ -24940,42 +25553,162 @@ "regjsparser": "bin/parser" } }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "license": "ISC" }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/renderkid/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } }, "node_modules/require-package-name": { "version": "2.0.1", @@ -25129,13 +25862,13 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", - "dev": true, + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", + "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", + "devOptional": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -25145,27 +25878,36 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.2.3", + "@rolldown/binding-darwin-arm64": "1.2.3", + "@rolldown/binding-darwin-x64": "1.2.3", + "@rolldown/binding-freebsd-x64": "1.2.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", + "@rolldown/binding-linux-arm64-gnu": "1.2.3", + "@rolldown/binding-linux-arm64-musl": "1.2.3", + "@rolldown/binding-linux-ppc64-gnu": "1.2.3", + "@rolldown/binding-linux-s390x-gnu": "1.2.3", + "@rolldown/binding-linux-x64-gnu": "1.2.3", + "@rolldown/binding-linux-x64-musl": "1.2.3", + "@rolldown/binding-openharmony-arm64": "1.2.3", + "@rolldown/binding-win32-arm64-msvc": "1.2.3", + "@rolldown/binding-win32-x64-msvc": "1.2.3" + } + }, + "node_modules/rolldown/node_modules/@oxc-project/types": { + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, "node_modules/rollup": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", - "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -25179,31 +25921,32 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.61.1", - "@rollup/rollup-android-arm64": "4.61.1", - "@rollup/rollup-darwin-arm64": "4.61.1", - "@rollup/rollup-darwin-x64": "4.61.1", - "@rollup/rollup-freebsd-arm64": "4.61.1", - "@rollup/rollup-freebsd-x64": "4.61.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", - "@rollup/rollup-linux-arm-musleabihf": "4.61.1", - "@rollup/rollup-linux-arm64-gnu": "4.61.1", - "@rollup/rollup-linux-arm64-musl": "4.61.1", - "@rollup/rollup-linux-loong64-gnu": "4.61.1", - "@rollup/rollup-linux-loong64-musl": "4.61.1", - "@rollup/rollup-linux-ppc64-gnu": "4.61.1", - "@rollup/rollup-linux-ppc64-musl": "4.61.1", - "@rollup/rollup-linux-riscv64-gnu": "4.61.1", - "@rollup/rollup-linux-riscv64-musl": "4.61.1", - "@rollup/rollup-linux-s390x-gnu": "4.61.1", - "@rollup/rollup-linux-x64-gnu": "4.61.1", - "@rollup/rollup-linux-x64-musl": "4.61.1", - "@rollup/rollup-openbsd-x64": "4.61.1", - "@rollup/rollup-openharmony-arm64": "4.61.1", - "@rollup/rollup-win32-arm64-msvc": "4.61.1", - "@rollup/rollup-win32-ia32-msvc": "4.61.1", - "@rollup/rollup-win32-x64-gnu": "4.61.1", - "@rollup/rollup-win32-x64-msvc": "4.61.1", + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", "fsevents": "~2.3.2" } }, @@ -25557,6 +26300,44 @@ "node": ">=11.0.0" } }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/scslre": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", @@ -26644,7 +27425,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -26721,7 +27502,7 @@ "version": "5.48.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -26740,7 +27521,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/text-decoder": { @@ -26858,9 +27639,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { @@ -28941,7 +29722,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -29021,6 +29802,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -29068,16 +29856,16 @@ } }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", "tinyglobby": "^0.2.17" }, "bin": { @@ -29094,7 +29882,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -29944,19 +30732,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -29984,12 +30772,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -30318,6 +31106,19 @@ "typescript": ">=5.0.0" } }, + "node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -30343,12 +31144,97 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, + "node_modules/webpack": { + "version": "5.109.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.109.2.tgz", + "integrity": "sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.24.4", + "es-module-lexer": "^2.1.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "graceful-fs": "^4.2.11", + "mime-db": "^1.54.0", + "minimizer-webpack-plugin": "^5.6.1", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "watchpack": "^2.5.2", + "webpack-sources": "^3.5.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", + "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", "license": "MIT" }, + "node_modules/webpack/node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -32152,6 +33038,199 @@ "node": ">=14.17" } }, + "packages/skew-protection-unplugin": { + "name": "@netlify/skew-protection-unplugin", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "unplugin": "^3.3.0" + }, + "devDependencies": { + "@types/node": "^20.19.43", + "html-webpack-plugin": "^5.6.8", + "rollup": "^4.62.4", + "tsup": "^8.5.1", + "vite": "^7.3.6", + "vitest": "^4.1.10", + "webpack": "^5.109.2" + }, + "engines": { + "node": "^20.6.1 || >=22" + }, + "peerDependencies": { + "html-webpack-plugin": "^5", + "rollup": "^3 || ^4", + "vite": "^4 || ^5 || ^6 || ^7", + "webpack": "^5" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "packages/skew-protection-unplugin/node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/skew-protection-unplugin/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "packages/skew-protection-unplugin/node_modules/unplugin": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", + "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.4", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "packages/skew-protection-unplugin/node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "packages/vite-plugin": { "name": "@netlify/vite-plugin", "version": "2.12.9", diff --git a/package.json b/package.json index a9936ce2..87651221 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "workspaces": [ "packages/angular-runtime", "packages/nuxt-module", + "packages/skew-protection-unplugin", "packages/vite-plugin", "packages/vite-plugin-tanstack-start" ], diff --git a/packages/skew-protection-unplugin/.gitignore b/packages/skew-protection-unplugin/.gitignore new file mode 100644 index 00000000..1eae0cf6 --- /dev/null +++ b/packages/skew-protection-unplugin/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/packages/skew-protection-unplugin/README.md b/packages/skew-protection-unplugin/README.md new file mode 100644 index 00000000..ee63d14f --- /dev/null +++ b/packages/skew-protection-unplugin/README.md @@ -0,0 +1,108 @@ +# @netlify/skew-protection-unplugin + +A bundler-agnostic plugin, built on [unplugin](https://unplugin.unjs.io/), that implements Netlify's +[Skew Protection](https://docs.netlify.com/deploy/deploy-overview#skew-protection) for frameworks not covered by any +Netlify-specific adapter. + +Skew protection pins a browser session's JS/CSS asset requests to the deploy that served the initial page, so open tabs +don't hit "loading chunk failed" errors (or a CSS/JS mismatch) when a newer deploy ships mid-session. + +## Bundler support + +| Bundler | Support | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| Vite ≤7 / Rollup | Stamps lazily-loaded chunks (via `renderDynamicImport`) and initial entry tags (via `transformIndexHtml`, Vite only) | +| Webpack 5 | Stamps lazily-loaded JS/CSS chunks (via a runtime module) and initial entry tags (via `html-webpack-plugin`, if present) | +| Vite 8+ (Rolldown), Rolldown, esbuild, Rspack, others | Not supported — see "Known limitations" | + +## Installation + +```bash +npm install -D @netlify/skew-protection-unplugin +``` + +## Usage + +Each bundler has its own entry point, so you only pull in the code for the bundler you're actually using: + +### Vite + +```ts +// vite.config.ts +import { defineConfig } from 'vite' +import skewProtection from '@netlify/skew-protection-unplugin/vite' + +export default defineConfig({ + plugins: [skewProtection()], +}) +``` + +### Webpack + +```js +// webpack.config.js +const skewProtection = require('@netlify/skew-protection-unplugin/webpack') + +module.exports = { + plugins: [skewProtection()], +} +``` + +### Rollup + +```js +// rollup.config.js +import skewProtection from '@netlify/skew-protection-unplugin/rollup' + +export default { + plugins: [skewProtection()], +} +``` + +Alternatively, import the default export from the package root and call `.vite()`/`.webpack()`/`.rollup()` on it, useful +if you need to target more than one bundler from the same module: + +```ts +import skewProtection from '@netlify/skew-protection-unplugin' + +skewProtection.vite() +skewProtection.webpack() +skewProtection.rollup() +``` + +## How it works + +- **Stamping**: lazily loaded JS chunks (via dynamic `import()`), lazily loaded CSS chunks (webpack only), and the + initial `', + '', + ].join('') + + const result = transformIndexHtml(html) + expect(result).toContain('href="/assets/index-abc.css?nfdpl=abc123"') + expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') + expect(result).toContain('href="/favicon.ico"') + }) + + test('stamps a lazily-loaded chunk in a real Vite build', async () => { + const root = await mkdtemp(path.join(tmpdir(), 'skew-protection-vite-')) + + try { + await writeFile(path.join(root, 'entry.js'), `import('./lazy.js').then((m) => console.log(m.default))`) + await writeFile(path.join(root, 'lazy.js'), `export default 'lazy chunk'`) + + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + await viteBuild({ + root, + logLevel: 'silent', + plugins: [{ name: 'skew-protection', ...createViteHooks(resolved) }], + build: { + outDir: 'dist', + rollupOptions: { + input: path.join(root, 'entry.js'), + }, + }, + }) + + const output = await readAllFileContents(path.join(root, 'dist')) + expect(output).toContain('?nfdpl=abc123') + } finally { + await rm(root, { recursive: true, force: true }) + } + }) +}) diff --git a/packages/skew-protection-unplugin/src/lib/vite-rollup.ts b/packages/skew-protection-unplugin/src/lib/vite-rollup.ts new file mode 100644 index 00000000..26fed7b8 --- /dev/null +++ b/packages/skew-protection-unplugin/src/lib/vite-rollup.ts @@ -0,0 +1,70 @@ +import type { Plugin as RollupPlugin } from 'rollup' +import type { Plugin as VitePlugin } from 'vite' + +import { appendQueryParam, compilePatterns, matchesAnyPattern } from './patterns.js' +import type { ResolvedSkewProtectionOptions } from './options.js' + +interface DynamicImportWrapper { + left: string + right: string +} + +/** + * Rollup/Vite dynamic `import()` calls always resolve to a JS chunk, but the final + * chunk filename isn't known yet when `renderDynamicImport` runs, so there's nothing + * meaningful to test patterns against per-call. Instead, gate on whether the configured + * patterns match JS assets at all. + * + * Returns a plain function rather than a `Partial`/`Partial` so + * `createRollupHooks` and `createViteHooks` can each type-check it against their own + * package's `Plugin` type independently — Vite 8+ defines its `Plugin` type against + * Rolldown rather than re-exporting Rollup's, so the two are no longer interchangeable. + */ +function createRenderDynamicImport(resolved: ResolvedSkewProtectionOptions): (() => DynamicImportWrapper) | undefined { + const regexps = compilePatterns(resolved.patterns) + + if (!matchesAnyPattern('__netlify_probe__.js', regexps)) { + return undefined + } + + const suffix = `?${resolved.paramName}=${encodeURIComponent(resolved.token)}` + + return () => ({ + left: 'import(', + right: ` + ${JSON.stringify(suffix)})`, + }) +} + +export function createRollupHooks(resolved: ResolvedSkewProtectionOptions): Partial { + const renderDynamicImport = createRenderDynamicImport(resolved) + return renderDynamicImport ? { renderDynamicImport } : {} +} + +export function createViteHooks(resolved: ResolvedSkewProtectionOptions): Partial { + const regexps = compilePatterns(resolved.patterns) + const renderDynamicImport = createRenderDynamicImport(resolved) + + return { + apply: 'build', + ...(renderDynamicImport ? { renderDynamicImport } : {}), + transformIndexHtml(html) { + return decorateHtml(html, resolved, regexps) + }, + } +} + +function decorateHtml(html: string, resolved: ResolvedSkewProtectionOptions, regexps: RegExp[]): string { + function decorateTag(tag: string, attribute: 'href' | 'src') { + return tag.replace(new RegExp(`${attribute}="([^"]+)"`), (match, url: string) => { + if (!matchesAnyPattern(url, regexps)) { + return match + } + + return `${attribute}="${appendQueryParam(url, resolved.paramName, resolved.token)}"` + }) + } + + return html + .replace(/]*>/g, (tag) => decorateTag(tag, 'src')) + .replace(/]*>/g, (tag) => decorateTag(tag, 'href')) +} diff --git a/packages/skew-protection-unplugin/src/lib/webpack.test.ts b/packages/skew-protection-unplugin/src/lib/webpack.test.ts new file mode 100644 index 00000000..c7eedce5 --- /dev/null +++ b/packages/skew-protection-unplugin/src/lib/webpack.test.ts @@ -0,0 +1,213 @@ +import { join } from 'node:path' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' + +import { afterEach, describe, expect, test } from 'vitest' +import HtmlWebpackPlugin from 'html-webpack-plugin' +import webpack, { type Compiler, type Configuration, type Stats } from 'webpack' + +import { applySkewProtectionWebpackPlugin } from './webpack.js' +import { assertDefined } from './test-utils.js' +import { resolveOptions } from './options.js' + +function compile(config: Configuration): Promise { + return new Promise((resolve, reject) => { + webpack(config, (err, stats) => { + if (err) { + reject(err) + return + } + + if (!stats) { + reject(new Error('webpack compiled without stats')) + return + } + + if (stats.hasErrors()) { + reject( + new Error( + stats.toString({ + errorDetails: true, + }), + ), + ) + + return + } + + resolve(stats) + }) + }) +} + +describe('applySkewProtectionWebpackPlugin', () => { + const dirsToClean: string[] = [] + + afterEach(async () => { + await Promise.all( + dirsToClean.splice(0).map((dir) => + rm(dir, { + force: true, + recursive: true, + }), + ), + ) + }) + + const setupFixture = async () => { + const root = await mkdtemp(join(tmpdir(), 'skew-protection-webpack-')) + dirsToClean.push(root) + await writeFile(join(root, 'index.js'), `import('./lazy.js').then((m) => console.log(m.default))`) + await writeFile(join(root, 'lazy.js'), `module.exports = 'lazy chunk'`) + return root + } + + test('wraps the chunk-loading runtime so lazily loaded chunks are requested with the query parameter', async () => { + const root = await setupFixture() + + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + const stats = await compile({ + context: root, + entry: join(root, 'index.js'), + mode: 'production', + optimization: { + minimize: false, + }, + output: { + filename: 'main.js', + path: join(root, 'dist'), + publicPath: '/assets/', + }, + plugins: [ + { + apply(compiler: Compiler) { + applySkewProtectionWebpackPlugin(compiler, resolved) + }, + }, + ], + }) + + const { chunks } = stats.toJson({ + chunks: true, + }) + + const lazyChunk = assertDefined(assertDefined(chunks).find((chunk) => !chunk.names.includes('main'))) + const lazyChunkFile = assertDefined(lazyChunk.files)[0] + + const mainBundle = await readFile(join(root, 'dist', 'main.js'), 'utf8') + expect(mainBundle).toContain('?nfdpl=abc123') + + // Execute the compiled runtime in a minimal DOM shim and confirm the script + // element created for the lazily loaded chunk carries the query parameter. + const capturedUrls: string[] = [] + + const context: { + [key: string]: unknown + self: unknown + } = { + clearTimeout, + console, + document: { + createElement: () => ({}), + getElementsByTagName: () => [], + head: { + appendChild(element: { src?: string }) { + return capturedUrls.push(element.src ?? '') + }, + }, + }, + module: { + exports: {}, + }, + require, + self: undefined, + setTimeout, + } + + context.self = context + const vm = await import('node:vm') + vm.createContext(context) + vm.runInContext(mainBundle, context, { + filename: 'main.js', + }) + + await new Promise((resolve) => setTimeout(resolve, 50)) + expect(capturedUrls).toEqual([`/assets/${lazyChunkFile}?nfdpl=abc123`]) + }) + + test('is a no-op when patterns do not match JS/CSS assets', async () => { + const root = await setupFixture() + + const resolved = assertDefined( + resolveOptions({ + patterns: ['.*\\.wasm$'], + token: 'abc123', + }), + ) + + await compile({ + context: root, + entry: join(root, 'index.js'), + mode: 'production', + optimization: { + minimize: false, + }, + output: { + filename: 'main.js', + path: join(root, 'dist'), + publicPath: '/assets/', + }, + plugins: [ + { + apply(compiler: Compiler) { + applySkewProtectionWebpackPlugin(compiler, resolved) + }, + }, + ], + }) + + const mainBundle = await readFile(join(root, 'dist', 'main.js'), 'utf8') + expect(mainBundle).not.toContain('nfdpl') + }) + + test('decorates initial script/link tags emitted by html-webpack-plugin', async () => { + const root = await setupFixture() + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + await compile({ + context: root, + entry: join(root, 'index.js'), + mode: 'production', + optimization: { + minimize: false, + }, + output: { + filename: 'main.js', + path: join(root, 'dist'), + publicPath: '/assets/', + }, + plugins: [ + new HtmlWebpackPlugin(), + { + apply(compiler: Compiler) { + applySkewProtectionWebpackPlugin(compiler, resolved) + }, + }, + ], + }) + + const html = await readFile(join(root, 'dist', 'index.html'), 'utf8') + expect(html).toContain('nfdpl=abc123') + }) +}) diff --git a/packages/skew-protection-unplugin/src/lib/webpack.ts b/packages/skew-protection-unplugin/src/lib/webpack.ts new file mode 100644 index 00000000..69e73f9a --- /dev/null +++ b/packages/skew-protection-unplugin/src/lib/webpack.ts @@ -0,0 +1,116 @@ +import { createRequire } from 'node:module' + +import type { Chunk, Compiler } from 'webpack' + +import { compilePatterns, matchesAnyPattern } from './patterns.js' +import type { ResolvedSkewProtectionOptions } from './options.js' + +const PLUGIN_NAME = 'netlify-skew-protection' + +/** + * Applies skew protection to a webpack compiler: + * - wraps the chunk-loading filename functions (`__webpack_require__.u`/`.k`) so lazily + * loaded JS/CSS chunks are requested with the deploy-pinning query parameter + * - decorates the initial `', - '', - ].join('') - - const result = transformIndexHtml(html) - expect(result).toContain('href="/assets/index-abc.css?nfdpl=abc123"') - expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') - expect(result).toContain('href="/favicon.ico"') - }) - - test('stamps a lazily-loaded chunk in a real Vite build', async () => { - const root = await mkdtemp(path.join(tmpdir(), 'skew-protection-vite-')) - - try { - await writeFile(path.join(root, 'entry.js'), `import('./lazy.js').then((m) => console.log(m.default))`) - await writeFile(path.join(root, 'lazy.js'), `export default 'lazy chunk'`) - - const resolved = assertDefined( - resolveOptions({ - paramName: 'nfdpl', - token: 'abc123', - }), - ) - - await viteBuild({ - root, - logLevel: 'silent', - plugins: [{ name: 'skew-protection', ...createViteHooks(resolved) }], - build: { - outDir: 'dist', - rollupOptions: { - input: path.join(root, 'entry.js'), - }, - }, - }) - - const output = await readAllFileContents(path.join(root, 'dist')) - expect(output).toContain('?nfdpl=abc123') - } finally { - await rm(root, { recursive: true, force: true }) - } - }) -}) diff --git a/packages/unplugin-skew-protection/src/lib/vite-rollup.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.ts similarity index 74% rename from packages/unplugin-skew-protection/src/lib/vite-rollup.ts rename to packages/unplugin-skew-protection/src/lib/render-chunk.ts index aeb73bad..3949829d 100644 --- a/packages/unplugin-skew-protection/src/lib/vite-rollup.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.ts @@ -1,7 +1,6 @@ import MagicString from 'magic-string' -import type { Plugin as VitePlugin } from 'vite' -import { appendQueryParam, compilePatterns, matchesAnyPattern } from './patterns.js' +import { compilePatterns, matchesAnyPattern } from './patterns.js' import type { ResolvedSkewProtectionOptions } from './options.js' const DYNAMIC_IMPORT_RE = /import\((["'`])([^"'`]+)\1\)/g @@ -16,7 +15,7 @@ interface NormalizedSourceMap { } type RenderChunkResult = { code: string; map: NormalizedSourceMap } | null -type RenderChunkHook = (code: string) => RenderChunkResult +export type RenderChunkHook = (code: string) => RenderChunkResult /** * Stamps dynamic `import()` call sites in already-rendered chunk code. @@ -37,7 +36,7 @@ type RenderChunkHook = (code: string) => RenderChunkResult * Rolldown define their own `Plugin` type rather than re-exporting Rollup's, so the * three aren't interchangeable at the type level (even though they're runtime-compatible). */ -function createRenderChunk(resolved: ResolvedSkewProtectionOptions): RenderChunkHook { +export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): RenderChunkHook { const regexps = compilePatterns(resolved.patterns) const suffix = `?${resolved.paramName}=${encodeURIComponent(resolved.token)}` @@ -91,31 +90,3 @@ function createRenderChunk(resolved: ResolvedSkewProtectionOptions): RenderChunk export function createRollupHooks(resolved: ResolvedSkewProtectionOptions): { renderChunk: RenderChunkHook } { return { renderChunk: createRenderChunk(resolved) } } - -export function createViteHooks(resolved: ResolvedSkewProtectionOptions): Partial { - const regexps = compilePatterns(resolved.patterns) - - return { - apply: 'build', - renderChunk: createRenderChunk(resolved), - transformIndexHtml(html) { - return decorateHtml(html, resolved, regexps) - }, - } -} - -function decorateHtml(html: string, resolved: ResolvedSkewProtectionOptions, regexps: RegExp[]): string { - function decorateTag(tag: string, attribute: 'href' | 'src') { - return tag.replace(new RegExp(`${attribute}="([^"]+)"`), (match, url: string) => { - if (!matchesAnyPattern(url, regexps)) { - return match - } - - return `${attribute}="${appendQueryParam(url, resolved.paramName, resolved.token)}"` - }) - } - - return html - .replace(/]*>/g, (tag) => decorateTag(tag, 'src')) - .replace(/]*>/g, (tag) => decorateTag(tag, 'href')) -} diff --git a/packages/unplugin-skew-protection/src/lib/vite.test.ts b/packages/unplugin-skew-protection/src/lib/vite.test.ts new file mode 100644 index 00000000..be2f383c --- /dev/null +++ b/packages/unplugin-skew-protection/src/lib/vite.test.ts @@ -0,0 +1,93 @@ +import { mkdtemp, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import path from 'node:path' + +import { build as viteBuild } from 'vite' +import { describe, expect, test } from 'vitest' + +import { assertDefined } from './test-utils.js' +import { createViteHooks } from './vite.js' +import { resolveOptions } from './options.js' + +async function readAllFileContents(dir: string): Promise { + const relativePaths = await readdir(dir, { recursive: true }) + const contents = await Promise.all( + relativePaths.map(async (relativePath) => { + const fullPath = path.join(dir, relativePath) + const stats = await stat(fullPath) + return stats.isFile() ? readFile(fullPath, 'utf8') : '' + }), + ) + return contents.join('\n') +} + +describe('createViteHooks', () => { + test('only applies during build', () => { + const resolved = assertDefined( + resolveOptions({ + token: 'abc123', + }), + ) + + expect(createViteHooks(resolved).apply).toBe('build') + }) + + test('decorates initial ', + '', + ].join('') + + const result = transformIndexHtml(html) + expect(result).toContain('href="/assets/index-abc.css?nfdpl=abc123"') + expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') + expect(result).toContain('href="/favicon.ico"') + }) + + test('stamps a lazily-loaded chunk in a real Vite build', async () => { + const root = await mkdtemp(path.join(tmpdir(), 'skew-protection-vite-')) + + try { + await writeFile(path.join(root, 'entry.js'), `import('./lazy.js').then((m) => console.log(m.default))`) + await writeFile(path.join(root, 'lazy.js'), `export default 'lazy chunk'`) + + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + await viteBuild({ + root, + logLevel: 'silent', + plugins: [{ name: 'skew-protection', ...createViteHooks(resolved) }], + build: { + outDir: 'dist', + rollupOptions: { + input: path.join(root, 'entry.js'), + }, + }, + }) + + const output = await readAllFileContents(path.join(root, 'dist')) + expect(output).toContain('?nfdpl=abc123') + } finally { + await rm(root, { recursive: true, force: true }) + } + }) +}) diff --git a/packages/unplugin-skew-protection/src/lib/vite.ts b/packages/unplugin-skew-protection/src/lib/vite.ts new file mode 100644 index 00000000..e495f5df --- /dev/null +++ b/packages/unplugin-skew-protection/src/lib/vite.ts @@ -0,0 +1,33 @@ +import type { Plugin as VitePlugin } from 'vite' + +import { appendQueryParam, compilePatterns, matchesAnyPattern } from './patterns.js' +import { createRenderChunk } from './render-chunk.js' +import type { ResolvedSkewProtectionOptions } from './options.js' + +export function createViteHooks(resolved: ResolvedSkewProtectionOptions): Partial { + const regexps = compilePatterns(resolved.patterns) + + return { + apply: 'build', + renderChunk: createRenderChunk(resolved), + transformIndexHtml(html) { + return decorateHtml(html, resolved, regexps) + }, + } +} + +function decorateHtml(html: string, resolved: ResolvedSkewProtectionOptions, regexps: RegExp[]): string { + function decorateTag(tag: string, attribute: 'href' | 'src') { + return tag.replace(new RegExp(`${attribute}="([^"]+)"`), (match, url: string) => { + if (!matchesAnyPattern(url, regexps)) { + return match + } + + return `${attribute}="${appendQueryParam(url, resolved.paramName, resolved.token)}"` + }) + } + + return html + .replace(/]*>/g, (tag) => decorateTag(tag, 'src')) + .replace(/]*>/g, (tag) => decorateTag(tag, 'href')) +} diff --git a/packages/unplugin-skew-protection/src/main.ts b/packages/unplugin-skew-protection/src/main.ts index a96e492c..5beb87dd 100644 --- a/packages/unplugin-skew-protection/src/main.ts +++ b/packages/unplugin-skew-protection/src/main.ts @@ -1,7 +1,8 @@ import { createUnplugin, type UnpluginOptions } from 'unplugin' import { applySkewProtectionWebpackPlugin } from './lib/webpack.js' -import { createRollupHooks, createViteHooks } from './lib/vite-rollup.js' +import { createRollupHooks } from './lib/render-chunk.js' +import { createViteHooks } from './lib/vite.js' import { resolveOptions, type SkewProtectionOptions } from './lib/options.js' import { writeManifest } from './lib/manifest.js' From 7b8231cb6bf75afba62d2e0fea2b7d90491264fa Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 09:09:36 +0530 Subject: [PATCH 05/18] coderabbit 1 --- package-lock.json | 250 ++++++++++-------- packages/unplugin-skew-protection/README.md | 9 +- .../unplugin-skew-protection/package.json | 7 +- .../src/lib/patterns.test.ts | 8 + .../src/lib/patterns.ts | 7 +- .../src/lib/render-chunk.test.ts | 50 +++- .../src/lib/render-chunk.ts | 79 +++--- .../unplugin-skew-protection/src/lib/vite.ts | 4 +- 8 files changed, 246 insertions(+), 168 deletions(-) diff --git a/package-lock.json b/package-lock.json index c7a69a66..7c82a611 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8342,10 +8342,26 @@ "dev": true, "license": "MIT" }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", + "integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", - "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", + "integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==", "cpu": [ "arm64" ], @@ -8359,9 +8375,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", + "integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==", "cpu": [ "arm64" ], @@ -8375,9 +8391,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz", - "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", + "integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==", "cpu": [ "x64" ], @@ -8391,9 +8407,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz", - "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", + "integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==", "cpu": [ "x64" ], @@ -8407,9 +8423,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz", - "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", + "integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==", "cpu": [ "arm" ], @@ -8423,9 +8439,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz", - "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", + "integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==", "cpu": [ "arm64" ], @@ -8442,9 +8458,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz", - "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", + "integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==", "cpu": [ "arm64" ], @@ -8461,9 +8477,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz", - "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", + "integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==", "cpu": [ "ppc64" ], @@ -8480,9 +8496,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz", - "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", + "integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==", "cpu": [ "s390x" ], @@ -8499,9 +8515,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz", - "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", + "integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==", "cpu": [ "x64" ], @@ -8518,9 +8534,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz", - "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", + "integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==", "cpu": [ "x64" ], @@ -8537,9 +8553,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz", - "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", + "integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==", "cpu": [ "arm64" ], @@ -8553,9 +8569,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz", - "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", + "integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==", "cpu": [ "arm64" ], @@ -8569,9 +8585,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz", - "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", + "integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==", "cpu": [ "x64" ], @@ -10133,16 +10149,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", - "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -10151,13 +10167,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", - "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.10", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -10178,9 +10194,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", - "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -10191,13 +10207,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", - "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.10", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -10205,14 +10221,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", - "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.10", - "@vitest/utils": "4.1.10", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -10221,9 +10237,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", - "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -10231,13 +10247,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", - "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.10", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -25862,13 +25878,13 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz", - "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz", + "integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==", "devOptional": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.144.0", + "@oxc-project/types": "=0.146.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -25878,26 +25894,27 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.2.4", - "@rolldown/binding-darwin-arm64": "1.2.4", - "@rolldown/binding-darwin-x64": "1.2.4", - "@rolldown/binding-freebsd-x64": "1.2.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", - "@rolldown/binding-linux-arm64-gnu": "1.2.4", - "@rolldown/binding-linux-arm64-musl": "1.2.4", - "@rolldown/binding-linux-ppc64-gnu": "1.2.4", - "@rolldown/binding-linux-s390x-gnu": "1.2.4", - "@rolldown/binding-linux-x64-gnu": "1.2.4", - "@rolldown/binding-linux-x64-musl": "1.2.4", - "@rolldown/binding-openharmony-arm64": "1.2.4", - "@rolldown/binding-win32-arm64-msvc": "1.2.4", - "@rolldown/binding-win32-x64-msvc": "1.2.4" + "@rolldown/binding-android-arm-eabi": "1.2.5", + "@rolldown/binding-android-arm64": "1.2.5", + "@rolldown/binding-darwin-arm64": "1.2.5", + "@rolldown/binding-darwin-x64": "1.2.5", + "@rolldown/binding-freebsd-x64": "1.2.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", + "@rolldown/binding-linux-arm64-gnu": "1.2.5", + "@rolldown/binding-linux-arm64-musl": "1.2.5", + "@rolldown/binding-linux-ppc64-gnu": "1.2.5", + "@rolldown/binding-linux-s390x-gnu": "1.2.5", + "@rolldown/binding-linux-x64-gnu": "1.2.5", + "@rolldown/binding-linux-x64-musl": "1.2.5", + "@rolldown/binding-openharmony-arm64": "1.2.5", + "@rolldown/binding-win32-arm64-msvc": "1.2.5", + "@rolldown/binding-win32-x64-msvc": "1.2.5" } }, "node_modules/rolldown/node_modules/@oxc-project/types": { - "version": "0.144.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz", - "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==", + "version": "0.146.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz", + "integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==", "devOptional": true, "license": "MIT", "funding": { @@ -30731,19 +30748,19 @@ } }, "node_modules/vitest": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", - "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.10", - "@vitest/mocker": "4.1.10", - "@vitest/pretty-format": "4.1.10", - "@vitest/runner": "4.1.10", - "@vitest/snapshot": "4.1.10", - "@vitest/spy": "4.1.10", - "@vitest/utils": "4.1.10", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -30771,12 +30788,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.10", - "@vitest/browser-preview": "4.1.10", - "@vitest/browser-webdriverio": "4.1.10", - "@vitest/coverage-istanbul": "4.1.10", - "@vitest/coverage-v8": "4.1.10", - "@vitest/ui": "4.1.10", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -33042,17 +33059,18 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "magic-string": "^1.2.1", + "es-module-lexer": "^2.3.2", + "magic-string": "^1.2.2", "unplugin": "^3.3.0" }, "devDependencies": { "@types/node": "^20.19.43", "html-webpack-plugin": "^5.6.8", - "rolldown": "^1.2.4", + "rolldown": "^1.2.5", "rollup": "^4.62.4", "tsup": "^8.5.1", "vite": "^8.2.1", - "vitest": "^4.1.10", + "vitest": "^4.1.11", "webpack": "^5.109.2" }, "engines": { @@ -33093,10 +33111,16 @@ "undici-types": "~6.21.0" } }, + "packages/unplugin-skew-protection/node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "license": "MIT" + }, "packages/unplugin-skew-protection/node_modules/magic-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.1.tgz", - "integrity": "sha512-vCfXkt3lIJha02CjPT1igeysyHVfCsEpIeD20O+X9aJ2hML3/kKx8E9Iv1FB+aMSAlDOEAtpRWzuooQCrYwdUg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.2.tgz", + "integrity": "sha512-veT/+7iXrXzT39XnEN4lOxtNl72dMgJ8Lp+5Bd6YcMSWpb0n0MjBM8Uuooi6jgJr8dhUW2swQgBmoZVMni5SVg==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" diff --git a/packages/unplugin-skew-protection/README.md b/packages/unplugin-skew-protection/README.md index 7fe3d882..ed7a7e2e 100644 --- a/packages/unplugin-skew-protection/README.md +++ b/packages/unplugin-skew-protection/README.md @@ -42,9 +42,9 @@ export default defineConfig({ ```js // webpack.config.js -const skewProtection = require('@netlify/unplugin-skew-protection/webpack') +import skewProtection from '@netlify/unplugin-skew-protection/webpack' -module.exports = { +export default { plugins: [skewProtection()], } ``` @@ -71,8 +71,9 @@ export default { } ``` -Alternatively, import the default export from the package root and call `.rolldown()`/`.rollup()`/`.vite()`/`.webpack()`/ -on it, useful if you need to target more than one bundler from the same module: +Alternatively, import the default export from the package root and call +`.rolldown()`/`.rollup()`/`.vite()`/`.webpack()`/ on it, useful if you need to target more than one bundler from the +same module: ```ts import skewProtection from '@netlify/unplugin-skew-protection' diff --git a/packages/unplugin-skew-protection/package.json b/packages/unplugin-skew-protection/package.json index f7f04f4f..d4ba7dc2 100644 --- a/packages/unplugin-skew-protection/package.json +++ b/packages/unplugin-skew-protection/package.json @@ -49,15 +49,16 @@ "devDependencies": { "@types/node": "^20.19.43", "html-webpack-plugin": "^5.6.8", - "rolldown": "^1.2.4", + "rolldown": "^1.2.5", "rollup": "^4.62.4", "tsup": "^8.5.1", "vite": "^8.2.1", - "vitest": "^4.1.10", + "vitest": "^4.1.11", "webpack": "^5.109.2" }, "dependencies": { - "magic-string": "^1.2.1", + "es-module-lexer": "^2.3.2", + "magic-string": "^1.2.2", "unplugin": "^3.3.0" }, "peerDependencies": { diff --git a/packages/unplugin-skew-protection/src/lib/patterns.test.ts b/packages/unplugin-skew-protection/src/lib/patterns.test.ts index f380b54f..d9cf2272 100644 --- a/packages/unplugin-skew-protection/src/lib/patterns.test.ts +++ b/packages/unplugin-skew-protection/src/lib/patterns.test.ts @@ -29,4 +29,12 @@ describe('appendQueryParam', () => { test('encodes the token value', () => { expect(appendQueryParam('/assets/app.js', 'nfdpl', 'a b&c')).toBe('/assets/app.js?nfdpl=a%20b%26c') }) + + test('inserts before a URL fragment when the URL has no query string', () => { + expect(appendQueryParam('/assets/app.js#foo', 'nfdpl', 'abc')).toBe('/assets/app.js?nfdpl=abc#foo') + }) + + test('inserts before a URL fragment when the URL already has a query string', () => { + expect(appendQueryParam('/assets/app.js?v=1#foo', 'nfdpl', 'abc')).toBe('/assets/app.js?v=1&nfdpl=abc#foo') + }) }) diff --git a/packages/unplugin-skew-protection/src/lib/patterns.ts b/packages/unplugin-skew-protection/src/lib/patterns.ts index 012ec822..bd85c1e9 100644 --- a/packages/unplugin-skew-protection/src/lib/patterns.ts +++ b/packages/unplugin-skew-protection/src/lib/patterns.ts @@ -1,6 +1,9 @@ export function appendQueryParam(url: string, paramName: string, token: string): string { - const separator = url.includes('?') ? '&' : '?' - return `${url}${separator}${paramName}=${encodeURIComponent(token)}` + const hashIndex = url.indexOf('#') + const base = hashIndex === -1 ? url : url.slice(0, hashIndex) + const fragment = hashIndex === -1 ? '' : url.slice(hashIndex) + const separator = base.includes('?') ? '&' : '?' + return `${base}${separator}${paramName}=${encodeURIComponent(token)}${fragment}` } export function compilePatterns(patterns: string[]): RegExp[] { diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts index 7326bf8a..f7a65115 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts @@ -3,7 +3,7 @@ import { rolldown } from 'rolldown' import { rollup, type OutputChunk } from 'rollup' import { assertDefined } from './test-utils.js' -import { createRollupHooks } from './render-chunk.js' +import { createRenderChunk, createRollupHooks } from './render-chunk.js' import { resolveOptions } from './options.js' function isEntryChunk(chunk: { isEntry?: boolean; type: string }): chunk is OutputChunk { @@ -131,3 +131,51 @@ describe('createRollupHooks', () => { expect(entryChunk.code).toContain('?nfdpl=abc123') }) }) + +describe('createRenderChunk', () => { + test('ignores dynamic-import-shaped text inside comments', async () => { + const resolved = assertDefined(resolveOptions({ paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `// import('lazy.js')\n/* import('lazy.js') */\nconsole.log('noop')` + expect(await renderChunk(code)).toBeNull() + }) + + test('ignores dynamic-import-shaped text inside string literals', async () => { + const resolved = assertDefined(resolveOptions({ paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `console.log("import('lazy.js')")` + expect(await renderChunk(code)).toBeNull() + }) + + test('stamps a real dynamic import that sits alongside comments and string literals', async () => { + const resolved = assertDefined(resolveOptions({ paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport('lazy.js')` + const result = assertDefined(await renderChunk(code)) + + expect(result.code).toBe( + `// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport('lazy.js?nfdpl=abc123')`, + ) + }) + + test('stamps a dynamic import that passes an options argument', async () => { + const resolved = assertDefined(resolveOptions({ patterns: ['.*\\.json$'], paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `import('lazy.json', { with: { type: 'json' } })` + const result = assertDefined(await renderChunk(code)) + + expect(result.code).toBe(`import('lazy.json?nfdpl=abc123', { with: { type: 'json' } })`) + }) + + test('is a no-op for a dynamic import with a non-literal specifier', async () => { + const resolved = assertDefined(resolveOptions({ paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `const path = 'lazy.js'; import(path)` + expect(await renderChunk(code)).toBeNull() + }) +}) diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.ts index 3949829d..ee10e32d 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.ts @@ -1,61 +1,55 @@ +import { init, parse } from 'es-module-lexer' import MagicString from 'magic-string' import { compilePatterns, matchesAnyPattern } from './patterns.js' import type { ResolvedSkewProtectionOptions } from './options.js' -const DYNAMIC_IMPORT_RE = /import\((["'`])([^"'`]+)\1\)/g - interface NormalizedSourceMap { - version: number file?: string + mappings: string + names: string[] sources: string[] sourcesContent?: string[] - names: string[] - mappings: string + version: number } type RenderChunkResult = { code: string; map: NormalizedSourceMap } | null -export type RenderChunkHook = (code: string) => RenderChunkResult +export type RenderChunkHook = (code: string) => Promise -/** - * Stamps dynamic `import()` call sites in already-rendered chunk code. - * - * Uses `renderChunk` rather than `renderDynamicImport` because Rolldown — the bundler - * Vite 8+ runs on — never invokes `renderDynamicImport` (confirmed by calling Rolldown - * directly), while `renderChunk` is a universal hook supported by Rollup, Rolldown, and - * every bundler built on either. Using it everywhere means one mechanism instead of two. - * - * By the time `renderChunk` runs, the target's real (hashed) specifier is already in the - * code, so — unlike the old `renderDynamicImport`-based approach — each match can be - * tested against `patterns` individually instead of gating the whole hook on/off with a - * synthetic probe string. - * - * Returns a plain function rather than a `Partial`/`Partial`/ - * `Partial` so `createRollupHooks` and `createViteHooks` can each - * type-check it against their own package's `Plugin` type independently — Vite 8+ and - * Rolldown define their own `Plugin` type rather than re-exporting Rollup's, so the - * three aren't interchangeable at the type level (even though they're runtime-compatible). - */ +// Stamps dynamic `import()` call sites in already-rendered chunk code export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): RenderChunkHook { const regexps = compilePatterns(resolved.patterns) const suffix = `?${resolved.paramName}=${encodeURIComponent(resolved.token)}` - return (code) => { + return async (code) => { if (!code.includes('import(')) { return null } + await init + + const [imports] = parse(code) let magicString: MagicString | undefined - for (const match of code.matchAll(DYNAMIC_IMPORT_RE)) { - const [, quote, specifier] = match + for (const imp of imports) { + // `d > -1` marks a dynamic `import(...)` call site (as opposed to a static import or + // `import.meta`); `n` is only populated when the specifier is a plain string literal, which + // excludes comments, string/template literals elsewhere in the code, and non-literal + // specifiers (e.g. `import(someVariable)`) that can't be matched against `patterns`. + if (imp.d === -1 || imp.n === undefined) { + continue + } + + const specifier = imp.n + if (specifier.includes(suffix) || !matchesAnyPattern(specifier, regexps)) { continue } + // For a dynamic `import()`, `s`/`e` span the specifier including its quotes (unlike + // static imports, where they exclude them), so insert just before the closing quote. magicString ??= new MagicString(code) - const specifierEnd = match.index + 'import('.length + quote.length + specifier.length - magicString.appendLeft(specifierEnd, suffix) + magicString.appendLeft(imp.e - 1, suffix) } if (!magicString) { @@ -63,30 +57,29 @@ export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): Rend } // See https://rolldown.rs/apis/plugin-api/transformations#transforming-a-chunk - const map = magicString.generateMap({ hires: 'boundary' }) + const map = magicString.generateMap({ + hires: 'boundary', + }) + return { code: magicString.toString(), map: { - version: map.version, file: map.file, + mappings: map.mappings, + names: map.names, sources: map.sources, // magic-string types this as `(string | null)[]`; Rollup/Rolldown expect `string[]`. sourcesContent: map.sourcesContent?.map((content) => content ?? ''), - names: map.names, - mappings: map.mappings, + version: map.version, }, } } } -/** - * Shared by the `rollup` and `rolldown` unplugin targets. Typed as a minimal, - * package-agnostic shape — not `Partial` — because unplugin only merges - * each target's own same-named field (e.g. `rolldown` only reads `UnpluginOptions.rolldown`), - * so this same value gets assigned to both `Partial` and `Partial` - * at the call site; typing it against either package specifically would reintroduce the - * cross-package coupling `createRenderChunk` above already avoids. - */ +// Shared by Rollup and Rolldown targets, kept package-agnostic +// to avoid coupling the shared value to either plugin type. export function createRollupHooks(resolved: ResolvedSkewProtectionOptions): { renderChunk: RenderChunkHook } { - return { renderChunk: createRenderChunk(resolved) } + return { + renderChunk: createRenderChunk(resolved) + } } diff --git a/packages/unplugin-skew-protection/src/lib/vite.ts b/packages/unplugin-skew-protection/src/lib/vite.ts index e495f5df..110e6907 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.ts @@ -28,6 +28,6 @@ function decorateHtml(html: string, resolved: ResolvedSkewProtectionOptions, reg } return html - .replace(/]*>/g, (tag) => decorateTag(tag, 'src')) - .replace(/]*>/g, (tag) => decorateTag(tag, 'href')) + .replace(/]*>/gi, (tag) => decorateTag(tag, 'src')) + .replace(/]*>/gi, (tag) => decorateTag(tag, 'href')) } From 8b3e09af5e620d227c3bed8926979f362c0f4563 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 10:01:54 +0530 Subject: [PATCH 06/18] coderabbit 2 --- README.md | 14 +++---- packages/unplugin-skew-protection/README.md | 5 +-- .../src/lib/render-chunk.test.ts | 4 +- .../src/lib/render-chunk.ts | 2 +- .../src/lib/vite.test.ts | 41 +++++++++++++++++++ .../unplugin-skew-protection/src/lib/vite.ts | 17 +++++--- .../src/lib/webpack.test.ts | 7 +++- 7 files changed, 68 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index d2be4353..2180b069 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ npm run dev ## Packages -| Name | Description | Version | -| ----------------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| 🅰️ [@netlify/angular-runtime](packages/angular-runtime) | Netlify Angular Runtime - run Angular seamlessly on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/angular-runtime.svg)](https://www.npmjs.com/package/@netlify/angular-runtime) | -| 🚀 [@netlify/nuxt](packages/nuxt-module) | Nuxt module with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/nuxt.svg)](https://www.npmjs.com/package/@netlify/nuxt) | -| 🔌 [@netlify/unplugin-skew-protection](packages/unplugin-skew-protection) | Bundler-agnostic plugin for Netlify Skew Protection | [![npm version](https://img.shields.io/npm/v/@netlify/unplugin-skew-protection.svg)](https://www.npmjs.com/package/@netlify/unplugin-skew-protection) | -| 🔌 [@netlify/vite-plugin](packages/vite-plugin) | Vite plugin with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin.svg)](https://www.npmjs.com/package/@netlify/vite-plugin) | -| 🔌 [@netlify/vite-plugin-tanstack-start](packages/vite-plugin-tanstack-start) | Vite plugin for TanStack Start on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin.svg)](https://www.npmjs.com/package/@netlify/vite-plugin-tanstack-start) | +| Name | Description | Version | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 🅰️ [@netlify/angular-runtime](packages/angular-runtime) | Netlify Angular Runtime - run Angular seamlessly on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/angular-runtime.svg)](https://www.npmjs.com/package/@netlify/angular-runtime) | +| 🚀 [@netlify/nuxt](packages/nuxt-module) | Nuxt module with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/nuxt.svg)](https://www.npmjs.com/package/@netlify/nuxt) | +| 🔌 [@netlify/unplugin-skew-protection](packages/unplugin-skew-protection) | Bundler-agnostic plugin for Netlify Skew Protection | [![npm version](https://img.shields.io/npm/v/@netlify/unplugin-skew-protection.svg)](https://www.npmjs.com/package/@netlify/unplugin-skew-protection) | +| 🔌 [@netlify/vite-plugin](packages/vite-plugin) | Vite plugin with a local emulation of the Netlify environment | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin.svg)](https://www.npmjs.com/package/@netlify/vite-plugin) | +| 🔌 [@netlify/vite-plugin-tanstack-start](packages/vite-plugin-tanstack-start) | Vite plugin for TanStack Start on Netlify | [![npm version](https://img.shields.io/npm/v/@netlify/vite-plugin-tanstack-start.svg)](https://www.npmjs.com/package/@netlify/vite-plugin-tanstack-start) | diff --git a/packages/unplugin-skew-protection/README.md b/packages/unplugin-skew-protection/README.md index ed7a7e2e..9a7d2be0 100644 --- a/packages/unplugin-skew-protection/README.md +++ b/packages/unplugin-skew-protection/README.md @@ -71,9 +71,8 @@ export default { } ``` -Alternatively, import the default export from the package root and call -`.rolldown()`/`.rollup()`/`.vite()`/`.webpack()`/ on it, useful if you need to target more than one bundler from the -same module: +Alternatively, import the default export from the package root and call `.rolldown()`/`.rollup()`/`.vite()`/`.webpack()` +on it, useful if you need to target more than one bundler from the same module: ```ts import skewProtection from '@netlify/unplugin-skew-protection' diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts index f7a65115..12932a24 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts @@ -156,9 +156,7 @@ describe('createRenderChunk', () => { const code = `// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport('lazy.js')` const result = assertDefined(await renderChunk(code)) - expect(result.code).toBe( - `// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport('lazy.js?nfdpl=abc123')`, - ) + expect(result.code).toBe(`// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport('lazy.js?nfdpl=abc123')`) }) test('stamps a dynamic import that passes an options argument', async () => { diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.ts index ee10e32d..e56eb528 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.ts @@ -80,6 +80,6 @@ export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): Rend // to avoid coupling the shared value to either plugin type. export function createRollupHooks(resolved: ResolvedSkewProtectionOptions): { renderChunk: RenderChunkHook } { return { - renderChunk: createRenderChunk(resolved) + renderChunk: createRenderChunk(resolved), } } diff --git a/packages/unplugin-skew-protection/src/lib/vite.test.ts b/packages/unplugin-skew-protection/src/lib/vite.test.ts index be2f383c..323a0c8b 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.test.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.test.ts @@ -58,6 +58,47 @@ describe('createViteHooks', () => { expect(result).toContain('href="/favicon.ico"') }) + test('decorates uppercase tags and single-quoted attributes', () => { + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + const hooks = createViteHooks(resolved) + const transformIndexHtml = assertDefined(hooks.transformIndexHtml) as (html: string) => string + + const html = [ + '', + "", + "", + '', + ].join('') + + const result = transformIndexHtml(html) + expect(result).toContain("href='/assets/index-abc.css?nfdpl=abc123'") + expect(result).toContain("src='/assets/index-abc.js?nfdpl=abc123'") + }) + + test('does not mistake a data-src attribute for src', () => { + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + const hooks = createViteHooks(resolved) + const transformIndexHtml = assertDefined(hooks.transformIndexHtml) as (html: string) => string + + const html = '' + + const result = transformIndexHtml(html) + expect(result).toContain('data-src="/assets/lazy-preview.js"') + expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') + }) + test('stamps a lazily-loaded chunk in a real Vite build', async () => { const root = await mkdtemp(path.join(tmpdir(), 'skew-protection-vite-')) diff --git a/packages/unplugin-skew-protection/src/lib/vite.ts b/packages/unplugin-skew-protection/src/lib/vite.ts index 110e6907..a8f99577 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.ts @@ -18,13 +18,18 @@ export function createViteHooks(resolved: ResolvedSkewProtectionOptions): Partia function decorateHtml(html: string, resolved: ResolvedSkewProtectionOptions, regexps: RegExp[]): string { function decorateTag(tag: string, attribute: 'href' | 'src') { - return tag.replace(new RegExp(`${attribute}="([^"]+)"`), (match, url: string) => { - if (!matchesAnyPattern(url, regexps)) { - return match - } + // Anchors the attribute name, supports both quote styles via the backreference, and matches + // tag/attribute names case-insensitively. + return tag.replace( + new RegExp(`(? { + if (!matchesAnyPattern(url, regexps)) { + return match + } - return `${attribute}="${appendQueryParam(url, resolved.paramName, resolved.token)}"` - }) + return `${attribute}=${quote}${appendQueryParam(url, resolved.paramName, resolved.token)}${quote}` + }, + ) } return html diff --git a/packages/unplugin-skew-protection/src/lib/webpack.test.ts b/packages/unplugin-skew-protection/src/lib/webpack.test.ts index c7eedce5..aa98aece 100644 --- a/packages/unplugin-skew-protection/src/lib/webpack.test.ts +++ b/packages/unplugin-skew-protection/src/lib/webpack.test.ts @@ -125,7 +125,6 @@ describe('applySkewProtectionWebpackPlugin', () => { module: { exports: {}, }, - require, self: undefined, setTimeout, } @@ -146,6 +145,7 @@ describe('applySkewProtectionWebpackPlugin', () => { const resolved = assertDefined( resolveOptions({ + paramName: 'nfdpl', patterns: ['.*\\.wasm$'], token: 'abc123', }), @@ -173,7 +173,10 @@ describe('applySkewProtectionWebpackPlugin', () => { }) const mainBundle = await readFile(join(root, 'dist', 'main.js'), 'utf8') - expect(mainBundle).not.toContain('nfdpl') + // Asserts against the suffix this exact config would produce, rather than a bare + // paramName substring — so the check can't coincidentally pass if the default paramName + // ever changes to something that no longer happens to match the literal here. + expect(mainBundle).not.toContain(`?${resolved.paramName}=${resolved.token}`) }) test('decorates initial script/link tags emitted by html-webpack-plugin', async () => { From f0a411c0cc72426c9fea813d394f1a665b5ff476 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 10:14:07 +0530 Subject: [PATCH 07/18] coderabit 3 --- .../src/lib/render-chunk.test.ts | 22 +++++++++++++++++ .../src/lib/render-chunk.ts | 15 ++++++++---- .../src/lib/vite.test.ts | 24 +++++++++++++++++++ .../unplugin-skew-protection/src/lib/vite.ts | 15 ++++++++---- .../src/lib/webpack.ts | 5 ++-- 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts index 12932a24..2c0c6707 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts @@ -176,4 +176,26 @@ describe('createRenderChunk', () => { const code = `const path = 'lazy.js'; import(path)` expect(await renderChunk(code)).toBeNull() }) + + test('merges with an existing query string instead of appending a second "?"', async () => { + // A pattern without a `.js$` anchor, since the default patterns wouldn't match a specifier + // that already ends in a query string or fragment. + const resolved = assertDefined(resolveOptions({ patterns: ['^lazy\\.js'], paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `import('lazy.js?v=1')` + const result = assertDefined(await renderChunk(code)) + + expect(result.code).toBe(`import('lazy.js?v=1&nfdpl=abc123')`) + }) + + test('inserts before a URL fragment instead of appending after it', async () => { + const resolved = assertDefined(resolveOptions({ patterns: ['^lazy\\.js'], paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `import('lazy.js#foo')` + const result = assertDefined(await renderChunk(code)) + + expect(result.code).toBe(`import('lazy.js?nfdpl=abc123#foo')`) + }) }) diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.ts index e56eb528..16faf5e2 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.ts @@ -1,7 +1,7 @@ import { init, parse } from 'es-module-lexer' import MagicString from 'magic-string' -import { compilePatterns, matchesAnyPattern } from './patterns.js' +import { appendQueryParam, compilePatterns, matchesAnyPattern } from './patterns.js' import type { ResolvedSkewProtectionOptions } from './options.js' interface NormalizedSourceMap { @@ -19,7 +19,10 @@ export type RenderChunkHook = (code: string) => Promise // Stamps dynamic `import()` call sites in already-rendered chunk code export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): RenderChunkHook { const regexps = compilePatterns(resolved.patterns) - const suffix = `?${resolved.paramName}=${encodeURIComponent(resolved.token)}` + // Only used to detect specifiers this hook has already stamped (idempotency), not to build the + // replacement text — the actual query param is appended per-specifier via `appendQueryParam`, + // which correctly merges with any existing `?query` or `#fragment` already on the specifier. + const marker = `${resolved.paramName}=${encodeURIComponent(resolved.token)}` return async (code) => { if (!code.includes('import(')) { @@ -42,14 +45,16 @@ export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): Rend const specifier = imp.n - if (specifier.includes(suffix) || !matchesAnyPattern(specifier, regexps)) { + if (specifier.includes(marker) || !matchesAnyPattern(specifier, regexps)) { continue } // For a dynamic `import()`, `s`/`e` span the specifier including its quotes (unlike - // static imports, where they exclude them), so insert just before the closing quote. + // static imports, where they exclude them), so the raw specifier text is `s + 1` to `e - 1`. + const stamped = appendQueryParam(specifier, resolved.paramName, resolved.token) + magicString ??= new MagicString(code) - magicString.appendLeft(imp.e - 1, suffix) + magicString.overwrite(imp.s + 1, imp.e - 1, stamped) } if (!magicString) { diff --git a/packages/unplugin-skew-protection/src/lib/vite.test.ts b/packages/unplugin-skew-protection/src/lib/vite.test.ts index 323a0c8b..3f75120c 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.test.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.test.ts @@ -99,6 +99,30 @@ describe('createViteHooks', () => { expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') }) + test('decorates unquoted attribute values', () => { + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + const hooks = createViteHooks(resolved) + const transformIndexHtml = assertDefined(hooks.transformIndexHtml) as (html: string) => string + + const html = [ + '', + '', + '', + '', + '', + ].join('') + + const result = transformIndexHtml(html) + expect(result).toContain('href="/assets/index-abc.css?nfdpl=abc123"') + expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') + }) + test('stamps a lazily-loaded chunk in a real Vite build', async () => { const root = await mkdtemp(path.join(tmpdir(), 'skew-protection-vite-')) diff --git a/packages/unplugin-skew-protection/src/lib/vite.ts b/packages/unplugin-skew-protection/src/lib/vite.ts index a8f99577..81c68aad 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.ts @@ -18,16 +18,21 @@ export function createViteHooks(resolved: ResolvedSkewProtectionOptions): Partia function decorateHtml(html: string, resolved: ResolvedSkewProtectionOptions, regexps: RegExp[]): string { function decorateTag(tag: string, attribute: 'href' | 'src') { - // Anchors the attribute name, supports both quote styles via the backreference, and matches - // tag/attribute names case-insensitively. + // Anchors the attribute name, supports both quote styles via the backreference (or no quotes + // at all, per the HTML spec's unquoted-value syntax), and matches tag/attribute names + // case-insensitively. return tag.replace( - new RegExp(`(? { + new RegExp(`(?\`]+))`, 'i'), + (match, quote: string | undefined, quotedUrl: string | undefined, unquotedUrl: string | undefined) => { + const url = quotedUrl ?? unquotedUrl ?? '' if (!matchesAnyPattern(url, regexps)) { return match } - return `${attribute}=${quote}${appendQueryParam(url, resolved.paramName, resolved.token)}${quote}` + // Always quote the output: the appended `?param=token` suffix contains `=`, which HTML + // forbids in an unquoted attribute value. + const outputQuote = quote ?? '"' + return `${attribute}=${outputQuote}${appendQueryParam(url, resolved.paramName, resolved.token)}${outputQuote}` }, ) } diff --git a/packages/unplugin-skew-protection/src/lib/webpack.ts b/packages/unplugin-skew-protection/src/lib/webpack.ts index 69e73f9a..b4794373 100644 --- a/packages/unplugin-skew-protection/src/lib/webpack.ts +++ b/packages/unplugin-skew-protection/src/lib/webpack.ts @@ -2,7 +2,7 @@ import { createRequire } from 'node:module' import type { Chunk, Compiler } from 'webpack' -import { compilePatterns, matchesAnyPattern } from './patterns.js' +import { appendQueryParam, compilePatterns, matchesAnyPattern } from './patterns.js' import type { ResolvedSkewProtectionOptions } from './options.js' const PLUGIN_NAME = 'netlify-skew-protection' @@ -64,8 +64,7 @@ function stampAttribute( return } - const separator = url.includes('?') ? '&' : '?' - tag.attributes[attributeName] = `${url}${separator}${resolved.paramName}=${encodeURIComponent(resolved.token)}` + tag.attributes[attributeName] = appendQueryParam(url, resolved.paramName, resolved.token) } function wrapChunkFilenameFunctions(compiler: Compiler, suffix: string) { From dfff7c7e97c7983a994828769eb6b022ebb26dc6 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 10:32:26 +0530 Subject: [PATCH 08/18] coderabbit 4 --- .../src/lib/patterns.test.ts | 18 ++++++++- .../src/lib/patterns.ts | 16 ++++++++ .../src/lib/render-chunk.test.ts | 37 +++++++++++++++++-- .../src/lib/render-chunk.ts | 16 ++++---- 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/packages/unplugin-skew-protection/src/lib/patterns.test.ts b/packages/unplugin-skew-protection/src/lib/patterns.test.ts index d9cf2272..a7c0a659 100644 --- a/packages/unplugin-skew-protection/src/lib/patterns.test.ts +++ b/packages/unplugin-skew-protection/src/lib/patterns.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'vitest' -import { appendQueryParam, compilePatterns, matchesAnyPattern } from './patterns.js' +import { appendQueryParam, compilePatterns, hasQueryParam, matchesAnyPattern } from './patterns.js' describe('compilePatterns', () => { test('throws a clear error for an invalid regular expression', () => { @@ -38,3 +38,19 @@ describe('appendQueryParam', () => { expect(appendQueryParam('/assets/app.js?v=1#foo', 'nfdpl', 'abc')).toBe('/assets/app.js?v=1&nfdpl=abc#foo') }) }) + +describe('hasQueryParam', () => { + test('returns false when there is no query string', () => { + expect(hasQueryParam('/assets/app.js', 'nfdpl', 'abc123')).toBe(false) + }) + + test('returns true when the exact parameter is present', () => { + expect(hasQueryParam('/assets/app.js?nfdpl=abc123', 'nfdpl', 'abc123')).toBe(true) + expect(hasQueryParam('/assets/app.js?v=1&nfdpl=abc123', 'nfdpl', 'abc123')).toBe(true) + expect(hasQueryParam('/assets/app.js?nfdpl=abc123#foo', 'nfdpl', 'abc123')).toBe(true) + }) + + test('returns false when the marker only appears inside another parameter value', () => { + expect(hasQueryParam('/assets/app.js?debug=nfdpl=abc123', 'nfdpl', 'abc123')).toBe(false) + }) +}) diff --git a/packages/unplugin-skew-protection/src/lib/patterns.ts b/packages/unplugin-skew-protection/src/lib/patterns.ts index bd85c1e9..533db34a 100644 --- a/packages/unplugin-skew-protection/src/lib/patterns.ts +++ b/packages/unplugin-skew-protection/src/lib/patterns.ts @@ -6,6 +6,22 @@ export function appendQueryParam(url: string, paramName: string, token: string): return `${base}${separator}${paramName}=${encodeURIComponent(token)}${fragment}` } +export function hasQueryParam(url: string, paramName: string, token: string): boolean { + const hashIndex = url.indexOf('#') + const base = hashIndex === -1 ? url : url.slice(0, hashIndex) + const queryIndex = base.indexOf('?') + + if (queryIndex === -1) { + return false + } + + const target = `${paramName}=${encodeURIComponent(token)}` + return base + .slice(queryIndex + 1) + .split('&') + .includes(target) +} + export function compilePatterns(patterns: string[]): RegExp[] { return patterns.map((pattern) => { try { diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts index 2c0c6707..89be3247 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts @@ -156,7 +156,7 @@ describe('createRenderChunk', () => { const code = `// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport('lazy.js')` const result = assertDefined(await renderChunk(code)) - expect(result.code).toBe(`// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport('lazy.js?nfdpl=abc123')`) + expect(result.code).toBe(`// import('lazy.js')\nconsole.log("import('lazy.js')")\nimport("lazy.js?nfdpl=abc123")`) }) test('stamps a dynamic import that passes an options argument', async () => { @@ -166,7 +166,7 @@ describe('createRenderChunk', () => { const code = `import('lazy.json', { with: { type: 'json' } })` const result = assertDefined(await renderChunk(code)) - expect(result.code).toBe(`import('lazy.json?nfdpl=abc123', { with: { type: 'json' } })`) + expect(result.code).toBe(`import("lazy.json?nfdpl=abc123", { with: { type: 'json' } })`) }) test('is a no-op for a dynamic import with a non-literal specifier', async () => { @@ -186,7 +186,7 @@ describe('createRenderChunk', () => { const code = `import('lazy.js?v=1')` const result = assertDefined(await renderChunk(code)) - expect(result.code).toBe(`import('lazy.js?v=1&nfdpl=abc123')`) + expect(result.code).toBe(`import("lazy.js?v=1&nfdpl=abc123")`) }) test('inserts before a URL fragment instead of appending after it', async () => { @@ -196,6 +196,35 @@ describe('createRenderChunk', () => { const code = `import('lazy.js#foo')` const result = assertDefined(await renderChunk(code)) - expect(result.code).toBe(`import('lazy.js?nfdpl=abc123#foo')`) + expect(result.code).toBe(`import("lazy.js?nfdpl=abc123#foo")`) + }) + + test('stamps a specifier even when the marker text appears inside another query value', async () => { + const resolved = assertDefined(resolveOptions({ patterns: ['^lazy\\.js'], paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `import('lazy.js?debug=nfdpl=abc123')` + const result = assertDefined(await renderChunk(code)) + + expect(result.code).toBe(`import("lazy.js?debug=nfdpl=abc123&nfdpl=abc123")`) + }) + + test('does not re-stamp a specifier that already carries the exact marker', async () => { + const resolved = assertDefined(resolveOptions({ patterns: ['^lazy\\.js'], paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = `import('lazy.js?nfdpl=abc123')` + expect(await renderChunk(code)).toBeNull() + }) + + test('produces valid JavaScript for a specifier containing an escaped quote', async () => { + const resolved = assertDefined(resolveOptions({ paramName: 'nfdpl', token: 'abc123' })) + const renderChunk = createRenderChunk(resolved) + + const code = String.raw`import("./lazy\"chunk.js")` + const result = assertDefined(await renderChunk(code)) + + expect(result.code).toBe(String.raw`import("./lazy\"chunk.js?nfdpl=abc123")`) + expect(() => new Function(`return ${result.code}`)).not.toThrow() }) }) diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.ts index 16faf5e2..a617055f 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.ts @@ -1,7 +1,7 @@ import { init, parse } from 'es-module-lexer' import MagicString from 'magic-string' -import { appendQueryParam, compilePatterns, matchesAnyPattern } from './patterns.js' +import { appendQueryParam, compilePatterns, hasQueryParam, matchesAnyPattern } from './patterns.js' import type { ResolvedSkewProtectionOptions } from './options.js' interface NormalizedSourceMap { @@ -19,10 +19,6 @@ export type RenderChunkHook = (code: string) => Promise // Stamps dynamic `import()` call sites in already-rendered chunk code export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): RenderChunkHook { const regexps = compilePatterns(resolved.patterns) - // Only used to detect specifiers this hook has already stamped (idempotency), not to build the - // replacement text — the actual query param is appended per-specifier via `appendQueryParam`, - // which correctly merges with any existing `?query` or `#fragment` already on the specifier. - const marker = `${resolved.paramName}=${encodeURIComponent(resolved.token)}` return async (code) => { if (!code.includes('import(')) { @@ -45,16 +41,18 @@ export function createRenderChunk(resolved: ResolvedSkewProtectionOptions): Rend const specifier = imp.n - if (specifier.includes(marker) || !matchesAnyPattern(specifier, regexps)) { + // Detects an exact paramName=token query parameter to ensure idempotency + // and avoid matching marker-shaped values embedded in other parameters. + if (hasQueryParam(specifier, resolved.paramName, resolved.token) || !matchesAnyPattern(specifier, regexps)) { continue } - // For a dynamic `import()`, `s`/`e` span the specifier including its quotes (unlike - // static imports, where they exclude them), so the raw specifier text is `s + 1` to `e - 1`. const stamped = appendQueryParam(specifier, resolved.paramName, resolved.token) + // For dynamic import(), s/e include the quotes, while imp.n is decoded. + // Re-serialize it with JSON.stringify to safely handle escaped quotes and backslashes. magicString ??= new MagicString(code) - magicString.overwrite(imp.s + 1, imp.e - 1, stamped) + magicString.overwrite(imp.s, imp.e, JSON.stringify(stamped)) } if (!magicString) { From 2dba73801f245044880428f1018fc046c8cda657 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 10:41:47 +0530 Subject: [PATCH 09/18] fix lint --- .../unplugin-skew-protection/src/lib/render-chunk.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts index 89be3247..475c3c06 100644 --- a/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts +++ b/packages/unplugin-skew-protection/src/lib/render-chunk.test.ts @@ -1,3 +1,4 @@ +import { init, parse } from 'es-module-lexer' import { describe, expect, test } from 'vitest' import { rolldown } from 'rolldown' import { rollup, type OutputChunk } from 'rollup' @@ -225,6 +226,10 @@ describe('createRenderChunk', () => { const result = assertDefined(await renderChunk(code)) expect(result.code).toBe(String.raw`import("./lazy\"chunk.js?nfdpl=abc123")`) - expect(() => new Function(`return ${result.code}`)).not.toThrow() + + await init + const [imports] = parse(result.code) + expect(imports).toHaveLength(1) + expect(imports[0]?.n).toBe('./lazy"chunk.js?nfdpl=abc123') }) }) From 3b21f25c0487a7158a70ddf0666157c0aa2a9096 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 10:54:37 +0530 Subject: [PATCH 10/18] coderabbit 5 --- .../src/lib/webpack.test.ts | 37 ++++++++++ .../src/lib/webpack.ts | 69 ++++++++++++------- .../unplugin-skew-protection/src/main.test.ts | 45 +++++++++++- .../src/rolldown.test.ts | 12 +++- .../src/rollup.test.ts | 12 +++- .../unplugin-skew-protection/src/vite.test.ts | 12 +++- .../src/webpack.test.ts | 12 +++- 7 files changed, 170 insertions(+), 29 deletions(-) diff --git a/packages/unplugin-skew-protection/src/lib/webpack.test.ts b/packages/unplugin-skew-protection/src/lib/webpack.test.ts index aa98aece..e7207509 100644 --- a/packages/unplugin-skew-protection/src/lib/webpack.test.ts +++ b/packages/unplugin-skew-protection/src/lib/webpack.test.ts @@ -140,6 +140,43 @@ describe('applySkewProtectionWebpackPlugin', () => { expect(capturedUrls).toEqual([`/assets/${lazyChunkFile}?nfdpl=abc123`]) }) + test('only wraps the JS chunk-filename function when patterns match JS but not CSS', async () => { + const root = await setupFixture() + + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + patterns: ['.*\\.(js|mjs|cjs)$'], + token: 'abc123', + }), + ) + + await compile({ + context: root, + entry: join(root, 'index.js'), + mode: 'production', + optimization: { + minimize: false, + }, + output: { + filename: 'main.js', + path: join(root, 'dist'), + publicPath: '/assets/', + }, + plugins: [ + { + apply(compiler: Compiler) { + applySkewProtectionWebpackPlugin(compiler, resolved) + }, + }, + ], + }) + + const mainBundle = await readFile(join(root, 'dist', 'main.js'), 'utf8') + expect(mainBundle).toContain('__netlifyOrigChunkScriptFilename__') + expect(mainBundle).not.toContain('__netlifyOrigChunkCssFilename__') + }) + test('is a no-op when patterns do not match JS/CSS assets', async () => { const root = await setupFixture() diff --git a/packages/unplugin-skew-protection/src/lib/webpack.ts b/packages/unplugin-skew-protection/src/lib/webpack.ts index b4794373..3bc2c1b2 100644 --- a/packages/unplugin-skew-protection/src/lib/webpack.ts +++ b/packages/unplugin-skew-protection/src/lib/webpack.ts @@ -18,8 +18,11 @@ export function applySkewProtectionWebpackPlugin(compiler: Compiler, resolved: R const regexps = compilePatterns(resolved.patterns) const suffix = `?${resolved.paramName}=${encodeURIComponent(resolved.token)}` - if (matchesAnyPattern('__netlify_probe__.js', regexps) || matchesAnyPattern('__netlify_probe__.css', regexps)) { - wrapChunkFilenameFunctions(compiler, suffix) + const stampJs = matchesAnyPattern('__netlify_probe__.js', regexps) + const stampCss = matchesAnyPattern('__netlify_probe__.css', regexps) + + if (stampJs || stampCss) { + wrapChunkFilenameFunctions(compiler, suffix, { stampCss, stampJs }) } decorateHtmlWebpackPluginTags(compiler, resolved, regexps) @@ -67,28 +70,42 @@ function stampAttribute( tag.attributes[attributeName] = appendQueryParam(url, resolved.paramName, resolved.token) } -function wrapChunkFilenameFunctions(compiler: Compiler, suffix: string) { +function wrapChunkFilenameFunctions( + compiler: Compiler, + suffix: string, + { stampCss, stampJs }: { stampCss: boolean; stampJs: boolean }, +) { class SkewProtectionRuntimeModule extends compiler.webpack.RuntimeModule { constructor() { super('netlify skew protection', compiler.webpack.RuntimeModule.STAGE_ATTACH) } override generate(): string { - return compiler.webpack.Template.asString([ - `var __netlifySkewSuffix__ = ${JSON.stringify(suffix)};`, - `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} === "function") {`, - compiler.webpack.Template.indent([ - `var __netlifyOrigChunkScriptFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename};`, - `${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} = function (chunkId) { return __netlifyOrigChunkScriptFilename__(chunkId) + __netlifySkewSuffix__; };`, - ]), - '}', - `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkCssFilename} === "function") {`, - compiler.webpack.Template.indent([ - `var __netlifyOrigChunkCssFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkCssFilename};`, - `${compiler.webpack.RuntimeGlobals.getChunkCssFilename} = function (chunkId) { return __netlifyOrigChunkCssFilename__(chunkId) + __netlifySkewSuffix__; };`, - ]), - '}', - ]) + const lines = [`var __netlifySkewSuffix__ = ${JSON.stringify(suffix)};`] + + if (stampJs) { + lines.push( + `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} === "function") {`, + compiler.webpack.Template.indent([ + `var __netlifyOrigChunkScriptFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename};`, + `${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} = function (chunkId) { return __netlifyOrigChunkScriptFilename__(chunkId) + __netlifySkewSuffix__; };`, + ]), + '}', + ) + } + + if (stampCss) { + lines.push( + `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkCssFilename} === "function") {`, + compiler.webpack.Template.indent([ + `var __netlifyOrigChunkCssFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkCssFilename};`, + `${compiler.webpack.RuntimeGlobals.getChunkCssFilename} = function (chunkId) { return __netlifyOrigChunkCssFilename__(chunkId) + __netlifySkewSuffix__; };`, + ]), + '}', + ) + } + + return compiler.webpack.Template.asString(lines) } } @@ -104,12 +121,16 @@ function wrapChunkFilenameFunctions(compiler: Compiler, suffix: string) { compilation.addRuntimeModule(chunk, new SkewProtectionRuntimeModule()) } - compilation.hooks.runtimeRequirementInTree - .for(compiler.webpack.RuntimeGlobals.getChunkScriptFilename) - .tap(PLUGIN_NAME, patchChunk) + if (stampJs) { + compilation.hooks.runtimeRequirementInTree + .for(compiler.webpack.RuntimeGlobals.getChunkScriptFilename) + .tap(PLUGIN_NAME, patchChunk) + } - compilation.hooks.runtimeRequirementInTree - .for(compiler.webpack.RuntimeGlobals.getChunkCssFilename) - .tap(PLUGIN_NAME, patchChunk) + if (stampCss) { + compilation.hooks.runtimeRequirementInTree + .for(compiler.webpack.RuntimeGlobals.getChunkCssFilename) + .tap(PLUGIN_NAME, patchChunk) + } }) } diff --git a/packages/unplugin-skew-protection/src/main.test.ts b/packages/unplugin-skew-protection/src/main.test.ts index 221b4097..7bdce5db 100644 --- a/packages/unplugin-skew-protection/src/main.test.ts +++ b/packages/unplugin-skew-protection/src/main.test.ts @@ -1,11 +1,12 @@ import { env } from 'node:process' import { join } from 'node:path' -import { mkdtemp, readFile, rm } from 'node:fs/promises' +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { afterEach, describe, expect, test } from 'vitest' import { rolldown } from 'rolldown' import { rollup, type OutputChunk } from 'rollup' +import webpack from 'webpack' import { assertDefined } from './lib/test-utils.js' import skewProtection from './main.js' @@ -109,4 +110,46 @@ describe('skewProtection', () => { expect(entryChunk.code).toContain('?nfdpl=abc123') }) + + test('writes the skew-protection manifest for a webpack build', async () => { + const baseDir = await mkdtemp(join(tmpdir(), 'skew-protection-main-webpack-')) + dirsToClean.push(baseDir) + + await writeFile(join(baseDir, 'index.js'), `import('./lazy.js').then((m) => console.log(m.default))`) + await writeFile(join(baseDir, 'lazy.js'), `module.exports = 'lazy chunk'`) + + await new Promise((resolve, reject) => { + webpack( + { + context: baseDir, + entry: join(baseDir, 'index.js'), + mode: 'production', + optimization: { minimize: false }, + output: { filename: 'main.js', path: join(baseDir, 'dist') }, + plugins: [skewProtection.webpack({ baseDir, paramName: 'nfdpl', token: 'abc123' })], + }, + (err, stats) => { + if (err) { + reject(err) + return + } + + if (stats?.hasErrors()) { + reject(new Error(stats.toString({ errorDetails: true }))) + return + } + + resolve() + }, + ) + }) + + const manifest: unknown = JSON.parse( + await readFile(join(baseDir, '.netlify', 'v1', 'skew-protection.json'), 'utf8'), + ) + expect(manifest).toEqual({ + patterns: ['.*\\.(js|mjs|cjs)$', '.*\\.css$'], + sources: [{ type: 'query', name: 'nfdpl' }], + }) + }) }) diff --git a/packages/unplugin-skew-protection/src/rolldown.test.ts b/packages/unplugin-skew-protection/src/rolldown.test.ts index 81240089..f12b17f4 100644 --- a/packages/unplugin-skew-protection/src/rolldown.test.ts +++ b/packages/unplugin-skew-protection/src/rolldown.test.ts @@ -1,10 +1,20 @@ import { env } from 'node:process' -import { describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test } from 'vitest' import rolldownSkewProtection from './rolldown.js' describe('rolldown entry point', () => { + const originalToken = env.NETLIFY_SKEW_PROTECTION_TOKEN + + afterEach(() => { + if (originalToken === undefined) { + delete env.NETLIFY_SKEW_PROTECTION_TOKEN + } else { + env.NETLIFY_SKEW_PROTECTION_TOKEN = originalToken + } + }) + test('returns a Rolldown plugin object', () => { delete env.NETLIFY_SKEW_PROTECTION_TOKEN const plugin = rolldownSkewProtection() diff --git a/packages/unplugin-skew-protection/src/rollup.test.ts b/packages/unplugin-skew-protection/src/rollup.test.ts index 9973f35f..4416ac2e 100644 --- a/packages/unplugin-skew-protection/src/rollup.test.ts +++ b/packages/unplugin-skew-protection/src/rollup.test.ts @@ -1,10 +1,20 @@ import { env } from 'node:process' -import { describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test } from 'vitest' import rollupSkewProtection from './rollup.js' describe('rollup entry point', () => { + const originalToken = env.NETLIFY_SKEW_PROTECTION_TOKEN + + afterEach(() => { + if (originalToken === undefined) { + delete env.NETLIFY_SKEW_PROTECTION_TOKEN + } else { + env.NETLIFY_SKEW_PROTECTION_TOKEN = originalToken + } + }) + test('returns a Rollup plugin object', () => { delete env.NETLIFY_SKEW_PROTECTION_TOKEN const plugin = rollupSkewProtection() diff --git a/packages/unplugin-skew-protection/src/vite.test.ts b/packages/unplugin-skew-protection/src/vite.test.ts index 37a2a155..0968550a 100644 --- a/packages/unplugin-skew-protection/src/vite.test.ts +++ b/packages/unplugin-skew-protection/src/vite.test.ts @@ -1,10 +1,20 @@ import { env } from 'node:process' -import { describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test } from 'vitest' import viteSkewProtection from './vite.js' describe('vite entry point', () => { + const originalToken = env.NETLIFY_SKEW_PROTECTION_TOKEN + + afterEach(() => { + if (originalToken === undefined) { + delete env.NETLIFY_SKEW_PROTECTION_TOKEN + } else { + env.NETLIFY_SKEW_PROTECTION_TOKEN = originalToken + } + }) + test('returns a Vite plugin object', () => { delete env.NETLIFY_SKEW_PROTECTION_TOKEN const plugin = viteSkewProtection() diff --git a/packages/unplugin-skew-protection/src/webpack.test.ts b/packages/unplugin-skew-protection/src/webpack.test.ts index 101fc7e9..189fc865 100644 --- a/packages/unplugin-skew-protection/src/webpack.test.ts +++ b/packages/unplugin-skew-protection/src/webpack.test.ts @@ -1,10 +1,20 @@ import { env } from 'node:process' -import { describe, expect, test } from 'vitest' +import { afterEach, describe, expect, test } from 'vitest' import webpackSkewProtection from './webpack.js' describe('webpack entry point', () => { + const originalToken = env.NETLIFY_SKEW_PROTECTION_TOKEN + + afterEach(() => { + if (originalToken === undefined) { + delete env.NETLIFY_SKEW_PROTECTION_TOKEN + } else { + env.NETLIFY_SKEW_PROTECTION_TOKEN = originalToken + } + }) + test('returns a webpack plugin instance', () => { delete env.NETLIFY_SKEW_PROTECTION_TOKEN const plugin = webpackSkewProtection() From 1253baf249b78c2ebfce38d746ed5c615432f84e Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 12:35:53 +0530 Subject: [PATCH 11/18] coderabbit 6 --- .../src/lib/vite.test.ts | 18 ++++++++++++++++++ .../unplugin-skew-protection/src/lib/vite.ts | 8 +++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/unplugin-skew-protection/src/lib/vite.test.ts b/packages/unplugin-skew-protection/src/lib/vite.test.ts index 3f75120c..6684e073 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.test.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.test.ts @@ -99,6 +99,24 @@ describe('createViteHooks', () => { expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') }) + test('does not mistake a colon-namespaced data:src attribute for src', () => { + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + const hooks = createViteHooks(resolved) + const transformIndexHtml = assertDefined(hooks.transformIndexHtml) as (html: string) => string + + const html = '' + + const result = transformIndexHtml(html) + expect(result).toContain('data:src="/assets/lazy-preview.js"') + expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') + }) + test('decorates unquoted attribute values', () => { const resolved = assertDefined( resolveOptions({ diff --git a/packages/unplugin-skew-protection/src/lib/vite.ts b/packages/unplugin-skew-protection/src/lib/vite.ts index 81c68aad..f8f25918 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.ts @@ -18,11 +18,13 @@ export function createViteHooks(resolved: ResolvedSkewProtectionOptions): Partia function decorateHtml(html: string, resolved: ResolvedSkewProtectionOptions, regexps: RegExp[]): string { function decorateTag(tag: string, attribute: 'href' | 'src') { - // Anchors the attribute name, supports both quote styles via the backreference (or no quotes - // at all, per the HTML spec's unquoted-value syntax), and matches tag/attribute names + // Anchors the attribute name to a preceding whitespace/tag-start boundary (rather than just + // excluding word chars and hyphens) so a colon-namespaced attribute like `data:src` doesn't + // get mistaken for `src`. Supports both quote styles via the backreference (or no quotes at + // all, per the HTML spec's unquoted-value syntax), and matches tag/attribute names // case-insensitively. return tag.replace( - new RegExp(`(?\`]+))`, 'i'), + new RegExp(`(?\`]+))`, 'i'), (match, quote: string | undefined, quotedUrl: string | undefined, unquotedUrl: string | undefined) => { const url = quotedUrl ?? unquotedUrl ?? '' if (!matchesAnyPattern(url, regexps)) { From 21e5384b5084f7ed5e923ccf0bd88298945abf5d Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 16:04:13 +0530 Subject: [PATCH 12/18] coderabbit 7 --- .../src/lib/patterns.ts | 29 ++--- .../src/lib/webpack.test.ts | 118 +++++++++++------- .../src/lib/webpack.ts | 90 ++++++------- 3 files changed, 135 insertions(+), 102 deletions(-) diff --git a/packages/unplugin-skew-protection/src/lib/patterns.ts b/packages/unplugin-skew-protection/src/lib/patterns.ts index 533db34a..9d1ad21b 100644 --- a/packages/unplugin-skew-protection/src/lib/patterns.ts +++ b/packages/unplugin-skew-protection/src/lib/patterns.ts @@ -6,6 +6,20 @@ export function appendQueryParam(url: string, paramName: string, token: string): return `${base}${separator}${paramName}=${encodeURIComponent(token)}${fragment}` } +export function compilePatterns(patterns: string[]): RegExp[] { + return patterns.map((pattern) => { + try { + return new RegExp(pattern) + } catch (error) { + const reason = error instanceof Error ? error.message : String(error) + + throw new Error(`Invalid skew protection pattern ${JSON.stringify(pattern)}: ${reason}`, { + cause: error, + }) + } + }) +} + export function hasQueryParam(url: string, paramName: string, token: string): boolean { const hashIndex = url.indexOf('#') const base = hashIndex === -1 ? url : url.slice(0, hashIndex) @@ -16,26 +30,13 @@ export function hasQueryParam(url: string, paramName: string, token: string): bo } const target = `${paramName}=${encodeURIComponent(token)}` + return base .slice(queryIndex + 1) .split('&') .includes(target) } -export function compilePatterns(patterns: string[]): RegExp[] { - return patterns.map((pattern) => { - try { - return new RegExp(pattern) - } catch (error) { - const reason = error instanceof Error ? error.message : String(error) - - throw new Error(`Invalid skew protection pattern ${JSON.stringify(pattern)}: ${reason}`, { - cause: error, - }) - } - }) -} - export function matchesAnyPattern(value: string, regexps: RegExp[]): boolean { return regexps.some((regexp) => regexp.test(value)) } diff --git a/packages/unplugin-skew-protection/src/lib/webpack.test.ts b/packages/unplugin-skew-protection/src/lib/webpack.test.ts index e7207509..383c6a4c 100644 --- a/packages/unplugin-skew-protection/src/lib/webpack.test.ts +++ b/packages/unplugin-skew-protection/src/lib/webpack.test.ts @@ -40,6 +40,46 @@ function compile(config: Configuration): Promise { }) } +// Executes a compiled webpack runtime in a minimal DOM shim and returns URLs +// requested for lazy chunks, letting tests verify actual chunk-loading behavior +// instead of searching bundle source text. + +async function captureRequestedUrls(mainBundle: string): Promise { + const capturedUrls: string[] = [] + + const context: { + [key: string]: unknown + self: unknown + } = { + clearTimeout, + console, + document: { + createElement: () => ({}), + getElementsByTagName: () => [], + head: { + appendChild(element: { src?: string }) { + return capturedUrls.push(element.src ?? '') + }, + }, + }, + module: { + exports: {}, + }, + self: undefined, + setTimeout, + } + + context.self = context + const vm = await import('node:vm') + vm.createContext(context) + vm.runInContext(mainBundle, context, { + filename: 'main.js', + }) + + await new Promise((resolve) => setTimeout(resolve, 50)) + return capturedUrls +} + describe('applySkewProtectionWebpackPlugin', () => { const dirsToClean: string[] = [] @@ -103,50 +143,31 @@ describe('applySkewProtectionWebpackPlugin', () => { const mainBundle = await readFile(join(root, 'dist', 'main.js'), 'utf8') expect(mainBundle).toContain('?nfdpl=abc123') - // Execute the compiled runtime in a minimal DOM shim and confirm the script - // element created for the lazily loaded chunk carries the query parameter. - const capturedUrls: string[] = [] - - const context: { - [key: string]: unknown - self: unknown - } = { - clearTimeout, - console, - document: { - createElement: () => ({}), - getElementsByTagName: () => [], - head: { - appendChild(element: { src?: string }) { - return capturedUrls.push(element.src ?? '') - }, - }, - }, - module: { - exports: {}, - }, - self: undefined, - setTimeout, - } - - context.self = context - const vm = await import('node:vm') - vm.createContext(context) - vm.runInContext(mainBundle, context, { - filename: 'main.js', - }) - - await new Promise((resolve) => setTimeout(resolve, 50)) + const capturedUrls = await captureRequestedUrls(mainBundle) expect(capturedUrls).toEqual([`/assets/${lazyChunkFile}?nfdpl=abc123`]) }) - test('only wraps the JS chunk-filename function when patterns match JS but not CSS', async () => { - const root = await setupFixture() + test('matches each chunk against the configured patterns individually, not by a fixed asset-type probe', async () => { + const root = await mkdtemp(join(tmpdir(), 'skew-protection-webpack-')) + dirsToClean.push(root) + + await writeFile( + join(root, 'index.js'), + [ + `import(/* webpackChunkName: "included" */ './included.js').then((m) => console.log(m.default))`, + `import(/* webpackChunkName: "excluded" */ './excluded.js').then((m) => console.log(m.default))`, + ].join('\n'), + ) + await writeFile(join(root, 'included.js'), `module.exports = 'included chunk'`) + await writeFile(join(root, 'excluded.js'), `module.exports = 'excluded chunk'`) const resolved = assertDefined( resolveOptions({ paramName: 'nfdpl', - patterns: ['.*\\.(js|mjs|cjs)$'], + // Matches only the "included" chunk's generated filename — not "excluded.js" or + // "main.js" — a shape the old fixed `__netlify_probe__.js`/`.css` classification + // couldn't honor, since it stamped (or skipped) an entire asset type at once. + patterns: ['^included\\.js$'], token: 'abc123', }), ) @@ -159,6 +180,7 @@ describe('applySkewProtectionWebpackPlugin', () => { minimize: false, }, output: { + chunkFilename: '[name].js', filename: 'main.js', path: join(root, 'dist'), publicPath: '/assets/', @@ -173,8 +195,10 @@ describe('applySkewProtectionWebpackPlugin', () => { }) const mainBundle = await readFile(join(root, 'dist', 'main.js'), 'utf8') - expect(mainBundle).toContain('__netlifyOrigChunkScriptFilename__') - expect(mainBundle).not.toContain('__netlifyOrigChunkCssFilename__') + const capturedUrls = await captureRequestedUrls(mainBundle) + + expect(capturedUrls).toContainEqual('/assets/included.js?nfdpl=abc123') + expect(capturedUrls).toContainEqual('/assets/excluded.js') }) test('is a no-op when patterns do not match JS/CSS assets', async () => { @@ -188,7 +212,7 @@ describe('applySkewProtectionWebpackPlugin', () => { }), ) - await compile({ + const stats = await compile({ context: root, entry: join(root, 'index.js'), mode: 'production', @@ -209,11 +233,17 @@ describe('applySkewProtectionWebpackPlugin', () => { ], }) + const { chunks } = stats.toJson({ + chunks: true, + }) + + const lazyChunk = assertDefined(assertDefined(chunks).find((chunk) => !chunk.names.includes('main'))) + const lazyChunkFile = assertDefined(lazyChunk.files)[0] + const mainBundle = await readFile(join(root, 'dist', 'main.js'), 'utf8') - // Asserts against the suffix this exact config would produce, rather than a bare - // paramName substring — so the check can't coincidentally pass if the default paramName - // ever changes to something that no longer happens to match the literal here. - expect(mainBundle).not.toContain(`?${resolved.paramName}=${resolved.token}`) + const capturedUrls = await captureRequestedUrls(mainBundle) + + expect(capturedUrls).toEqual([`/assets/${lazyChunkFile}`]) }) test('decorates initial script/link tags emitted by html-webpack-plugin', async () => { diff --git a/packages/unplugin-skew-protection/src/lib/webpack.ts b/packages/unplugin-skew-protection/src/lib/webpack.ts index 3bc2c1b2..a661aad4 100644 --- a/packages/unplugin-skew-protection/src/lib/webpack.ts +++ b/packages/unplugin-skew-protection/src/lib/webpack.ts @@ -16,15 +16,8 @@ const PLUGIN_NAME = 'netlify-skew-protection' export function applySkewProtectionWebpackPlugin(compiler: Compiler, resolved: ResolvedSkewProtectionOptions) { const regexps = compilePatterns(resolved.patterns) - const suffix = `?${resolved.paramName}=${encodeURIComponent(resolved.token)}` - - const stampJs = matchesAnyPattern('__netlify_probe__.js', regexps) - const stampCss = matchesAnyPattern('__netlify_probe__.css', regexps) - - if (stampJs || stampCss) { - wrapChunkFilenameFunctions(compiler, suffix, { stampCss, stampJs }) - } + wrapChunkFilenameFunctions(compiler, resolved, regexps) decorateHtmlWebpackPluginTags(compiler, resolved, regexps) } @@ -70,42 +63,55 @@ function stampAttribute( tag.attributes[attributeName] = appendQueryParam(url, resolved.paramName, resolved.token) } -function wrapChunkFilenameFunctions( - compiler: Compiler, - suffix: string, - { stampCss, stampJs }: { stampCss: boolean; stampJs: boolean }, -) { +// Chunk filenames are only known when webpack runs, so the configured patterns +// can't be checked at compile time. Instead, the compiled regexes are inlined +// into the runtime module and matched against each chunk's generated filename, +// ensuring patterns only stamp the chunks they actually match. + +function wrapChunkFilenameFunctions(compiler: Compiler, resolved: ResolvedSkewProtectionOptions, regexps: RegExp[]) { + const suffix = `?${resolved.paramName}=${encodeURIComponent(resolved.token)}` + const patternsLiteral = `[${regexps.map(String).join(', ')}]` + class SkewProtectionRuntimeModule extends compiler.webpack.RuntimeModule { constructor() { super('netlify skew protection', compiler.webpack.RuntimeModule.STAGE_ATTACH) } override generate(): string { - const lines = [`var __netlifySkewSuffix__ = ${JSON.stringify(suffix)};`] - - if (stampJs) { - lines.push( - `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} === "function") {`, + return compiler.webpack.Template.asString([ + `var __netlifySkewPatterns__ = ${patternsLiteral};`, + `var __netlifySkewSuffix__ = ${JSON.stringify(suffix)};`, + 'function __netlifySkewMatches__(filename) {', + compiler.webpack.Template.indent([ + 'for (var i = 0; i < __netlifySkewPatterns__.length; i++) {', + compiler.webpack.Template.indent('if (__netlifySkewPatterns__[i].test(filename)) return true;'), + '}', + 'return false;', + ]), + '}', + `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} === "function") {`, + compiler.webpack.Template.indent([ + `var __netlifyOrigChunkScriptFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename};`, + `${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} = function (chunkId) {`, compiler.webpack.Template.indent([ - `var __netlifyOrigChunkScriptFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkScriptFilename};`, - `${compiler.webpack.RuntimeGlobals.getChunkScriptFilename} = function (chunkId) { return __netlifyOrigChunkScriptFilename__(chunkId) + __netlifySkewSuffix__; };`, + 'var filename = __netlifyOrigChunkScriptFilename__(chunkId);', + 'return __netlifySkewMatches__(filename) ? filename + __netlifySkewSuffix__ : filename;', ]), - '}', - ) - } - - if (stampCss) { - lines.push( - `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkCssFilename} === "function") {`, + '};', + ]), + '}', + `if (typeof ${compiler.webpack.RuntimeGlobals.getChunkCssFilename} === "function") {`, + compiler.webpack.Template.indent([ + `var __netlifyOrigChunkCssFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkCssFilename};`, + `${compiler.webpack.RuntimeGlobals.getChunkCssFilename} = function (chunkId) {`, compiler.webpack.Template.indent([ - `var __netlifyOrigChunkCssFilename__ = ${compiler.webpack.RuntimeGlobals.getChunkCssFilename};`, - `${compiler.webpack.RuntimeGlobals.getChunkCssFilename} = function (chunkId) { return __netlifyOrigChunkCssFilename__(chunkId) + __netlifySkewSuffix__; };`, + 'var filename = __netlifyOrigChunkCssFilename__(chunkId);', + 'return __netlifySkewMatches__(filename) ? filename + __netlifySkewSuffix__ : filename;', ]), - '}', - ) - } - - return compiler.webpack.Template.asString(lines) + '};', + ]), + '}', + ]) } } @@ -121,16 +127,12 @@ function wrapChunkFilenameFunctions( compilation.addRuntimeModule(chunk, new SkewProtectionRuntimeModule()) } - if (stampJs) { - compilation.hooks.runtimeRequirementInTree - .for(compiler.webpack.RuntimeGlobals.getChunkScriptFilename) - .tap(PLUGIN_NAME, patchChunk) - } + compilation.hooks.runtimeRequirementInTree + .for(compiler.webpack.RuntimeGlobals.getChunkScriptFilename) + .tap(PLUGIN_NAME, patchChunk) - if (stampCss) { - compilation.hooks.runtimeRequirementInTree - .for(compiler.webpack.RuntimeGlobals.getChunkCssFilename) - .tap(PLUGIN_NAME, patchChunk) - } + compilation.hooks.runtimeRequirementInTree + .for(compiler.webpack.RuntimeGlobals.getChunkCssFilename) + .tap(PLUGIN_NAME, patchChunk) }) } From 51ecd147c686ef3b5678ae9b6b3fb145307c6c53 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 16:13:53 +0530 Subject: [PATCH 13/18] coderabbit 8 --- packages/unplugin-skew-protection/src/lib/webpack.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/unplugin-skew-protection/src/lib/webpack.test.ts b/packages/unplugin-skew-protection/src/lib/webpack.test.ts index 383c6a4c..ebd94a83 100644 --- a/packages/unplugin-skew-protection/src/lib/webpack.test.ts +++ b/packages/unplugin-skew-protection/src/lib/webpack.test.ts @@ -51,7 +51,10 @@ async function captureRequestedUrls(mainBundle: string): Promise { [key: string]: unknown self: unknown } = { - clearTimeout, + // Webpack's chunk-load runtime schedules a real timeout before `appendChild` and only clears + // it from the script's `onload`/`onerror` handlers, which this shim never invokes — using the + // real timers here would leave a live ~120s timer referenced after every call. + clearTimeout: () => undefined, console, document: { createElement: () => ({}), @@ -66,7 +69,7 @@ async function captureRequestedUrls(mainBundle: string): Promise { exports: {}, }, self: undefined, - setTimeout, + setTimeout: () => 0, } context.self = context From 15f25720184334a9df7ad6638179f05158f22e0e Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 17:19:25 +0530 Subject: [PATCH 14/18] use html parser for vite --- package-lock.json | 25 +++++++ .../unplugin-skew-protection/package.json | 9 +-- .../src/lib/vite.test.ts | 41 +++++++++++ .../unplugin-skew-protection/src/lib/vite.ts | 71 +++++++++++++------ 4 files changed, 119 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c3f15ed..b068a67f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23840,6 +23840,30 @@ "dev": true, "license": "MIT" }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -33061,6 +33085,7 @@ "dependencies": { "es-module-lexer": "^2.3.2", "magic-string": "^1.2.2", + "parse5": "^8.0.1", "unplugin": "^3.3.0" }, "devDependencies": { diff --git a/packages/unplugin-skew-protection/package.json b/packages/unplugin-skew-protection/package.json index d4ba7dc2..098a460c 100644 --- a/packages/unplugin-skew-protection/package.json +++ b/packages/unplugin-skew-protection/package.json @@ -10,11 +10,11 @@ "types": "./dist/main.d.ts", "exports": { ".": "./dist/main.js", - "./vite": "./dist/vite.js", - "./webpack": "./dist/webpack.js", - "./rollup": "./dist/rollup.js", + "./package.json": "./package.json", "./rolldown": "./dist/rolldown.js", - "./package.json": "./package.json" + "./rollup": "./dist/rollup.js", + "./vite": "./dist/vite.js", + "./webpack": "./dist/webpack.js" }, "files": [ "dist/**/*" @@ -59,6 +59,7 @@ "dependencies": { "es-module-lexer": "^2.3.2", "magic-string": "^1.2.2", + "parse5": "^8.0.1", "unplugin": "^3.3.0" }, "peerDependencies": { diff --git a/packages/unplugin-skew-protection/src/lib/vite.test.ts b/packages/unplugin-skew-protection/src/lib/vite.test.ts index 6684e073..dee0a518 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.test.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.test.ts @@ -117,6 +117,47 @@ describe('createViteHooks', () => { expect(result).toContain('src="/assets/index-abc.js?nfdpl=abc123"') }) + test('does not decorate a script tag written inside an HTML comment', () => { + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + const hooks = createViteHooks(resolved) + const transformIndexHtml = assertDefined(hooks.transformIndexHtml) as (html: string) => string + + const html = [ + '', + '', + ].join('\n') + + const result = transformIndexHtml(html) + expect(result).toContain('\n-->') + expect(result).toContain('src="/assets/index-real.js?nfdpl=abc123"') + }) + + test('does not decorate a string literal inside a script body that looks like a tag', () => { + const resolved = assertDefined( + resolveOptions({ + paramName: 'nfdpl', + token: 'abc123', + }), + ) + + const hooks = createViteHooks(resolved) + const transformIndexHtml = assertDefined(hooks.transformIndexHtml) as (html: string) => string + + const html = `` + + const result = transformIndexHtml(html) + expect(result).toContain('const s = "' + const result = transformIndexHtml(html) + expect(result).not.toContain('" onload="') + expect(result).toContain('src="/assets/app.js" onload="evil()?nfdpl=abc123"') + }) + test('decorates unquoted attribute values', () => { const resolved = assertDefined( resolveOptions({ diff --git a/packages/unplugin-skew-protection/src/lib/vite.ts b/packages/unplugin-skew-protection/src/lib/vite.ts index aaaab95f..976c4bb0 100644 --- a/packages/unplugin-skew-protection/src/lib/vite.ts +++ b/packages/unplugin-skew-protection/src/lib/vite.ts @@ -66,5 +66,15 @@ function decorateAttribute( const quote = /^[^=]+=(["'])/.exec(raw)?.[1] ?? '"' const stampedUrl = appendQueryParam(attr.value, resolved.paramName, resolved.token) - magicString.overwrite(location.startOffset, location.endOffset, `${attributeName}=${quote}${stampedUrl}${quote}`) + // parse5 decodes HTML entities in `attr.value` (e.g. `"` -> `"`), so a matching URL that + // contains an encoded quote/ampersand would otherwise be written back as literal markup — closing + // the attribute early and injecting whatever follows as a new attribute. + const escapedUrl = escapeAttributeValue(stampedUrl, quote) + + magicString.overwrite(location.startOffset, location.endOffset, `${attributeName}=${quote}${escapedUrl}${quote}`) +} + +function escapeAttributeValue(value: string, quote: string): string { + const quoteEntity = quote === "'" ? ''' : '"' + return value.replace(/&/g, '&').replace(/ Date: Thu, 20 Aug 2026 17:47:39 +0530 Subject: [PATCH 16/18] remove main export --- packages/unplugin-skew-protection/README.md | 46 +++++++------------ .../unplugin-skew-protection/package.json | 3 -- .../unplugin-skew-protection/src/main.test.ts | 14 +++--- packages/unplugin-skew-protection/src/main.ts | 15 ++---- .../unplugin-skew-protection/src/rolldown.ts | 3 ++ .../unplugin-skew-protection/src/rollup.ts | 3 ++ packages/unplugin-skew-protection/src/vite.ts | 3 ++ .../unplugin-skew-protection/src/webpack.ts | 3 ++ .../unplugin-skew-protection/tsup.config.ts | 2 +- 9 files changed, 42 insertions(+), 50 deletions(-) diff --git a/packages/unplugin-skew-protection/README.md b/packages/unplugin-skew-protection/README.md index 9a7d2be0..5ffa208a 100644 --- a/packages/unplugin-skew-protection/README.md +++ b/packages/unplugin-skew-protection/README.md @@ -26,23 +26,11 @@ npm install -D @netlify/unplugin-skew-protection Each bundler has its own entry point (`rolldown`, `rollup`, `vite`, `webpack`), so you only pull in the code for the bundler you're actually using: -### Vite - -```ts -// vite.config.ts -import { defineConfig } from 'vite' -import skewProtection from '@netlify/unplugin-skew-protection/vite' - -export default defineConfig({ - plugins: [skewProtection()], -}) -``` - -### Webpack +### Rollup ```js -// webpack.config.js -import skewProtection from '@netlify/unplugin-skew-protection/webpack' +// rollup.config.js +import skewProtection from '@netlify/unplugin-skew-protection/rollup' export default { plugins: [skewProtection()], @@ -60,27 +48,27 @@ export default { } ``` -### Rollup +### Vite -```js -// rollup.config.js -import skewProtection from '@netlify/unplugin-skew-protection/rollup' +```ts +// vite.config.ts +import { defineConfig } from 'vite' +import skewProtection from '@netlify/unplugin-skew-protection/vite' -export default { +export default defineConfig({ plugins: [skewProtection()], -} +}) ``` -Alternatively, import the default export from the package root and call `.rolldown()`/`.rollup()`/`.vite()`/`.webpack()` -on it, useful if you need to target more than one bundler from the same module: +### Webpack -```ts -import skewProtection from '@netlify/unplugin-skew-protection' +```js +// webpack.config.js +import skewProtection from '@netlify/unplugin-skew-protection/webpack' -skewProtection.rolldown() -skewProtection.rollup() -skewProtection.vite() -skewProtection.webpack() +export default { + plugins: [skewProtection()], +} ``` ## How it works diff --git a/packages/unplugin-skew-protection/package.json b/packages/unplugin-skew-protection/package.json index 098a460c..d0063f05 100644 --- a/packages/unplugin-skew-protection/package.json +++ b/packages/unplugin-skew-protection/package.json @@ -6,10 +6,7 @@ "engines": { "node": "^20.6.1 || >=22" }, - "main": "./dist/main.js", - "types": "./dist/main.d.ts", "exports": { - ".": "./dist/main.js", "./package.json": "./package.json", "./rolldown": "./dist/rolldown.js", "./rollup": "./dist/rollup.js", diff --git a/packages/unplugin-skew-protection/src/main.test.ts b/packages/unplugin-skew-protection/src/main.test.ts index 7bdce5db..e3d8da14 100644 --- a/packages/unplugin-skew-protection/src/main.test.ts +++ b/packages/unplugin-skew-protection/src/main.test.ts @@ -9,12 +9,14 @@ import { rollup, type OutputChunk } from 'rollup' import webpack from 'webpack' import { assertDefined } from './lib/test-utils.js' -import skewProtection from './main.js' +import rolldownSkewProtection from './rolldown.js' +import rollupSkewProtection from './rollup.js' +import webpackSkewProtection from './webpack.js' const isEntryChunk = (chunk: { type: string; isEntry?: boolean }): chunk is OutputChunk => chunk.type === 'chunk' && Boolean(chunk.isEntry) -describe('skewProtection', () => { +describe('unpluginFactory, exercised through each bundler entry point', () => { const dirsToClean: string[] = [] const originalToken = env.NETLIFY_SKEW_PROTECTION_TOKEN @@ -36,7 +38,7 @@ describe('skewProtection', () => { load: (id: string) => (id === 'entry.js' ? `console.log('hi')` : null), } - const bundle = await rollup({ input: 'entry.js', plugins: [virtualPlugin, skewProtection.rollup()] }) + const bundle = await rollup({ input: 'entry.js', plugins: [virtualPlugin, rollupSkewProtection()] }) const { output } = await bundle.generate({ format: 'es' }) const entryChunk = assertDefined(output.find(isEntryChunk)) expect(entryChunk.code).not.toContain('nfdpl') @@ -63,7 +65,7 @@ describe('skewProtection', () => { const bundle = await rollup({ input: 'entry.js', - plugins: [virtualPlugin, skewProtection.rollup({ token: 'abc123', paramName: 'nfdpl', baseDir })], + plugins: [virtualPlugin, rollupSkewProtection({ token: 'abc123', paramName: 'nfdpl', baseDir })], }) const { output } = await bundle.write({ format: 'es', dir: join(baseDir, 'dist') }) @@ -98,7 +100,7 @@ describe('skewProtection', () => { const bundle = await rolldown({ input: 'entry.js', - plugins: [virtualPlugin, skewProtection.rolldown({ token: 'abc123', paramName: 'nfdpl' })], + plugins: [virtualPlugin, rolldownSkewProtection({ token: 'abc123', paramName: 'nfdpl' })], }) const { output } = await bundle.generate({ format: 'es' }) @@ -126,7 +128,7 @@ describe('skewProtection', () => { mode: 'production', optimization: { minimize: false }, output: { filename: 'main.js', path: join(baseDir, 'dist') }, - plugins: [skewProtection.webpack({ baseDir, paramName: 'nfdpl', token: 'abc123' })], + plugins: [webpackSkewProtection({ baseDir, paramName: 'nfdpl', token: 'abc123' })], }, (err, stats) => { if (err) { diff --git a/packages/unplugin-skew-protection/src/main.ts b/packages/unplugin-skew-protection/src/main.ts index 5beb87dd..d63e7199 100644 --- a/packages/unplugin-skew-protection/src/main.ts +++ b/packages/unplugin-skew-protection/src/main.ts @@ -1,4 +1,4 @@ -import { createUnplugin, type UnpluginOptions } from 'unplugin' +import type { UnpluginOptions } from 'unplugin' import { applySkewProtectionWebpackPlugin } from './lib/webpack.js' import { createRollupHooks } from './lib/render-chunk.js' @@ -6,10 +6,9 @@ import { createViteHooks } from './lib/vite.js' import { resolveOptions, type SkewProtectionOptions } from './lib/options.js' import { writeManifest } from './lib/manifest.js' -export type { ResolvedSkewProtectionOptions, SkewProtectionOptions } from './lib/options.js' - -export type { SkewProtectionManifest } from './lib/manifest.js' - +// Internal factory shared by each bundler-specific entry point (./rollup.js, ./rolldown.js, +// ./vite.js, ./webpack.js) — this module has no public entry of its own, so users can only reach +// a single-bundler plugin via one of those subpaths, not a combined multi-bundler object. export function unpluginFactory(userOptions: SkewProtectionOptions | undefined = {}): UnpluginOptions { const resolved = resolveOptions(userOptions) @@ -34,9 +33,3 @@ export function unpluginFactory(userOptions: SkewProtectionOptions | undefined = }, } } - -const skewProtection = /* #__PURE__ */ createUnplugin(unpluginFactory) - -export default skewProtection - -export { skewProtection } diff --git a/packages/unplugin-skew-protection/src/rolldown.ts b/packages/unplugin-skew-protection/src/rolldown.ts index 674c9049..32347c73 100644 --- a/packages/unplugin-skew-protection/src/rolldown.ts +++ b/packages/unplugin-skew-protection/src/rolldown.ts @@ -2,4 +2,7 @@ import { createRolldownPlugin } from 'unplugin' import { unpluginFactory } from './main.js' +export type { ResolvedSkewProtectionOptions, SkewProtectionOptions } from './lib/options.js' +export type { SkewProtectionManifest } from './lib/manifest.js' + export default createRolldownPlugin(unpluginFactory) diff --git a/packages/unplugin-skew-protection/src/rollup.ts b/packages/unplugin-skew-protection/src/rollup.ts index 560e3b3a..62f3258e 100644 --- a/packages/unplugin-skew-protection/src/rollup.ts +++ b/packages/unplugin-skew-protection/src/rollup.ts @@ -2,4 +2,7 @@ import { createRollupPlugin } from 'unplugin' import { unpluginFactory } from './main.js' +export type { ResolvedSkewProtectionOptions, SkewProtectionOptions } from './lib/options.js' +export type { SkewProtectionManifest } from './lib/manifest.js' + export default createRollupPlugin(unpluginFactory) diff --git a/packages/unplugin-skew-protection/src/vite.ts b/packages/unplugin-skew-protection/src/vite.ts index 17582761..0eba4f79 100644 --- a/packages/unplugin-skew-protection/src/vite.ts +++ b/packages/unplugin-skew-protection/src/vite.ts @@ -2,4 +2,7 @@ import { createVitePlugin } from 'unplugin' import { unpluginFactory } from './main.js' +export type { ResolvedSkewProtectionOptions, SkewProtectionOptions } from './lib/options.js' +export type { SkewProtectionManifest } from './lib/manifest.js' + export default createVitePlugin(unpluginFactory) diff --git a/packages/unplugin-skew-protection/src/webpack.ts b/packages/unplugin-skew-protection/src/webpack.ts index c1aefb44..ab47b323 100644 --- a/packages/unplugin-skew-protection/src/webpack.ts +++ b/packages/unplugin-skew-protection/src/webpack.ts @@ -2,4 +2,7 @@ import { createWebpackPlugin } from 'unplugin' import { unpluginFactory } from './main.js' +export type { ResolvedSkewProtectionOptions, SkewProtectionOptions } from './lib/options.js' +export type { SkewProtectionManifest } from './lib/manifest.js' + export default createWebpackPlugin(unpluginFactory) diff --git a/packages/unplugin-skew-protection/tsup.config.ts b/packages/unplugin-skew-protection/tsup.config.ts index 57af16f1..e71c5411 100644 --- a/packages/unplugin-skew-protection/tsup.config.ts +++ b/packages/unplugin-skew-protection/tsup.config.ts @@ -7,7 +7,7 @@ export default defineConfig([ bundle: true, clean: true, dts: true, - entry: ['src/main.ts', 'src/rollup.ts', 'src/rolldown.ts', 'src/vite.ts', 'src/webpack.ts'], + entry: ['src/rolldown.ts', 'src/rollup.ts', 'src/vite.ts', 'src/webpack.ts'], external: ['html-webpack-plugin', 'rolldown', 'rollup', 'vite', 'webpack'], format: ['esm'], platform: 'node', From 0b392dd88cc2f434d262617e06b568459b62c09e Mon Sep 17 00:00:00 2001 From: Hrishikesh Kokate Date: Thu, 20 Aug 2026 18:25:04 +0530 Subject: [PATCH 17/18] add html support for rolldown/rollup --- package-lock.json | 19 ++ packages/unplugin-skew-protection/README.md | 17 +- .../unplugin-skew-protection/package.json | 1 + .../unplugin-skew-protection/src/lib/html.ts | 65 ++++++ .../src/lib/render-chunk.test.ts | 130 +---------- .../src/lib/render-chunk.ts | 8 - .../src/lib/rollup.test.ts | 207 ++++++++++++++++++ .../src/lib/rollup.ts | 61 ++++++ .../unplugin-skew-protection/src/lib/vite.ts | 66 +----- packages/unplugin-skew-protection/src/main.ts | 6 +- 10 files changed, 371 insertions(+), 209 deletions(-) create mode 100644 packages/unplugin-skew-protection/src/lib/html.ts create mode 100644 packages/unplugin-skew-protection/src/lib/rollup.test.ts create mode 100644 packages/unplugin-skew-protection/src/lib/rollup.ts diff --git a/package-lock.json b/package-lock.json index b068a67f..785994d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8659,6 +8659,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/plugin-html": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-html/-/plugin-html-2.0.0.tgz", + "integrity": "sha512-6S0ezKfDRw5FvHk3xm1T/eXP9IlLf82zmV+z2V1/yQL5hXwIUL/Gl2RrNMMIe2rt85rk6+0smvSAtjfTLGylDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-inject": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", @@ -33089,6 +33107,7 @@ "unplugin": "^3.3.0" }, "devDependencies": { + "@rollup/plugin-html": "^2.0.0", "@types/node": "^20.19.43", "html-webpack-plugin": "^5.6.8", "rolldown": "^1.2.5", diff --git a/packages/unplugin-skew-protection/README.md b/packages/unplugin-skew-protection/README.md index 5ffa208a..ef2c11df 100644 --- a/packages/unplugin-skew-protection/README.md +++ b/packages/unplugin-skew-protection/README.md @@ -9,11 +9,11 @@ don't hit "loading chunk failed" errors (or a CSS/JS mismatch) when a newer depl ## Bundler support -| Bundler | Support | -| ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| Rolldown / Rollup / Vite | Stamps lazily-loaded chunks (via `renderChunk`) and initial entry tags (via `transformIndexHtml`, Vite only) | -| Webpack 5 | Stamps lazily-loaded JS/CSS chunks (via a runtime module) and initial entry tags (via `html-webpack-plugin`, if present) | -| esbuild, Rspack, others | Not yet supported | +| Bundler | Support | +| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Rolldown / Rollup / Vite | Stamps lazily-loaded chunks (via `renderChunk`) and initial entry tags — via `transformIndexHtml` on Vite, or any `.html` asset another plugin emits (e.g. `@rollup/plugin-html`) on plain Rollup/Rolldown | +| Webpack 5 | Stamps lazily-loaded JS/CSS chunks (via a runtime module) and initial entry tags (via `html-webpack-plugin`, if present) | +| esbuild, Rspack, others | Not yet supported | ## Installation @@ -82,6 +82,13 @@ export default { [MagicString](https://github.com/rich-harris/magic-string) to keep sourcemaps accurate), rather than `renderDynamicImport` — Rolldown (the bundler Vite 8+ runs on) never invokes `renderDynamicImport`, so `renderChunk` is used everywhere for one consistent mechanism instead of two. + - On plain Rollup/Rolldown (no Vite), initial `