diff --git a/build.mjs b/build.mjs index 77773d00..c0ea5963 100644 --- a/build.mjs +++ b/build.mjs @@ -2,7 +2,37 @@ import { chmod, readFile, rename, stat, unlink, writeFile } from 'fs/promises'; import esbuild from 'esbuild'; import { sentryEsbuildPlugin } from '@sentry/esbuild-plugin'; -const plugins = []; +// jsonc-parser ships a UMD entry (its `main`) whose body does runtime +// `require("./impl/format")` etc. esbuild can't follow those dynamic sibling +// requires when bundling, so the built `dist/craft` fails at startup with +// "Cannot find module './impl/format'". Redirect the package to its ESM entry +// (`module`), which uses static imports esbuild can bundle. Pulled in +// transitively via @vercel/client → @vercel/microfrontends. +const jsoncParserEsmPlugin = { + name: 'jsonc-parser-esm', + setup(build) { + build.onResolve({ filter: /^jsonc-parser$/ }, async args => { + // Avoid recursing into our own resolve call below. + if (args.pluginData?.resolved) { + return; + } + const result = await build.resolve('jsonc-parser', { + importer: args.importer, + kind: args.kind, + resolveDir: args.resolveDir, + pluginData: { resolved: true }, + }); + if (result.errors.length > 0) { + return result; + } + return { + path: result.path.replace(/([\\/])lib[\\/]umd[\\/]/, '$1lib/esm/'), + }; + }); + }, +}; + +const plugins = [jsoncParserEsmPlugin]; // Only add Sentry plugin if auth token is available (production builds on master) if (process.env.SENTRY_AUTH_TOKEN) { diff --git a/docs/src/content/docs/targets/index.md b/docs/src/content/docs/targets/index.md index 8218e9c3..4cf1ab53 100644 --- a/docs/src/content/docs/targets/index.md +++ b/docs/src/content/docs/targets/index.md @@ -19,6 +19,7 @@ Targets define where Craft publishes your release artifacts. Configure them in ` | [GCS](./gcs/) | Upload to Google Cloud Storage | | [GitHub Pages](./gh-pages/) | Deploy static sites | | [Cloudflare](./cloudflare/) | Deploy static sites or Workers to Cloudflare | +| [Vercel](./vercel/) | Deploy a prebuilt static site to Vercel | | [CocoaPods](./cocoapods/) | Publish iOS/macOS pods | | [Ruby Gems](./gem/) | Publish Ruby gems | | [Maven](./maven/) | Publish to Maven Central | diff --git a/docs/src/content/docs/targets/vercel.md b/docs/src/content/docs/targets/vercel.md new file mode 100644 index 00000000..51e170a5 --- /dev/null +++ b/docs/src/content/docs/targets/vercel.md @@ -0,0 +1,60 @@ +--- +title: Vercel +description: Deploy a prebuilt static site to Vercel +--- + +Deploys a release artifact to [Vercel](https://vercel.com/) as a production deployment. + +The target extracts a ZIP artifact and deploys it via the Vercel deploy API (using [`@vercel/client`](https://www.npmjs.com/package/@vercel/client)) to promote it to production. It does not use or require the `vercel` CLI. + +## Configuration + +| Option | Description | +|--------|-------------| +| `prebuilt` | Whether the artifact contains a prebuilt `.vercel/output` (the result of `vercel build`). When `true` (default), the artifact's prebuilt `.vercel/output` is uploaded and the remote build step is skipped. Set to `false` to have Vercel build from source. | +| `workingDir` | Subdirectory within the extracted artifact to deploy from. | + +## Environment Variables + +| Name | Required | Description | +|------|----------|-------------| +| `VERCEL_TOKEN` | Yes | Vercel access token. Passed to the deploy API, never on the command line. | +| `VERCEL_ORG_ID` | No | Vercel organization/team ID. An identifier, not a secret. Forwarded to the deploy API as the team ID so the deployment links non-interactively. | +| `VERCEL_PROJECT_ID` | No | Vercel project ID. An identifier, not a secret. Forwarded to the deploy API as the project identifier so the deployment links to the intended project non-interactively. | + +:::note +For non-interactive CI deployments, set both `VERCEL_ORG_ID` and `VERCEL_PROJECT_ID` so the deploy API knows which project to deploy to. +::: + +## Default Behavior + +By default, this target: + +1. Looks for a single artifact matching `vercel.zip` (or `*-vercel.zip`). Override with `includeNames`. +2. Extracts its contents (preserving the archive layout, e.g. a top-level `.vercel/output`). +3. Deploys to production via the Vercel deploy API. + +The version being released is attached to the deployment as metadata (`release=`) for traceability. + +## Example + +```yaml +targets: + - name: vercel + # prebuilt defaults to true: the docs site is built in CI and this + # target only promotes the prebuilt output to production. +``` + +Deploying from a subdirectory of the artifact: + +```yaml +targets: + - name: vercel + workingDir: docs +``` + +## Workflow + +1. Build the site in CI (e.g. `vercel build`) and create a `vercel.zip` artifact containing the prebuilt `.vercel/output` (or the source when `prebuilt: false`). +2. Configure the target in `.craft.yml`. +3. Set `VERCEL_TOKEN` in your environment, plus `VERCEL_ORG_ID` and `VERCEL_PROJECT_ID` so the deploy targets the right project. diff --git a/package.json b/package.json index 8ad90194..9a8d029a 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "pnpm": "10.27.0" }, "dependencies": { + "@vercel/client": "^18.2.5", "fastest-levenshtein": "^1.0.16", "ignore": "^7.0.5", "marked": "^17.0.1", @@ -115,7 +116,10 @@ "form-data@>=4": "^4.0.6", "form-data@<3": "^2.5.6", "vite": "^7.3.5", - "@babel/core": "^7.29.6" + "@babel/core": "^7.29.6", + "js-yaml": "^4.3.0", + "path-to-regexp@<6.3.0": "^6.3.0", + "tar-fs@<1.16.4": "1.16.6" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 167cb907..233b7caf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,11 +19,17 @@ overrides: form-data@<3: ^2.5.6 vite: ^7.3.5 '@babel/core': ^7.29.6 + js-yaml: ^4.3.0 + path-to-regexp@<6.3.0: ^6.3.0 + tar-fs@<1.16.4: 1.16.6 importers: .: dependencies: + '@vercel/client': + specifier: ^18.2.5 + version: 18.2.5(vite@7.3.5(@types/node@24.13.2)(tsx@4.21.0)) fastest-levenshtein: specifier: ^1.0.16 version: 1.0.16 @@ -152,7 +158,7 @@ importers: specifier: ^2.0.0 version: 2.0.0 js-yaml: - specifier: 4.3.0 + specifier: ^4.3.0 version: 4.3.0 mkdirp: specifier: ^1.0.4 @@ -421,6 +427,9 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@bytecodealliance/preview2-shim@0.17.6': + resolution: {integrity: sha512-n3cM88gTen5980UOBAD6xDcNNL3ocTK8keab21bpx1ONdA+ARj7uD1qoFxOWCyKlkpSi195FH+GeAut7Oc6zZw==} + '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} @@ -833,9 +842,24 @@ packages: '@kwsites/promise-deferred@1.1.1': resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} + '@next/env@15.1.6': + resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==} + '@nodable/entities@2.1.0': resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@octokit/auth-token@5.1.2': resolution: {integrity: sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==} engines: {node: '>= 18'} @@ -1093,6 +1117,10 @@ packages: peerDependencies: '@opentelemetry/api': ^1.8 + '@renovatebot/pep440@4.2.1': + resolution: {integrity: sha512-2FK1hF93Fuf1laSdfiEmJvSJPVIDHEUTz68D3Fi9s0IZrrpaEcj6pTFBTbYvsgC5du4ogrtf5re7yMMvrKNgkw==} + engines: {node: ^20.9.0 || ^22.11.0 || ^24, pnpm: ^10.0.0} + '@rollup/rollup-android-arm-eabi@4.61.1': resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} cpu: [arm] @@ -1676,6 +1704,49 @@ packages: resolution: {integrity: sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vercel/build-utils@14.0.1': + resolution: {integrity: sha512-EGOVxPX5T6yYfITMZACezM0rDHByCXEJ7X42jGytl5bAYi/DSJR0S3PpRPOiqYOh46ml9LmRJyZeq5V13HeGDg==} + + '@vercel/client@18.2.5': + resolution: {integrity: sha512-T1Qgj0U9IfFgXMgR3K+bihYZAh9wDyFdgo8pyt00rCrrtBo820WPsrSYH0xz/qMwOUanOAOk3dhsEnG6spCIFg==} + engines: {node: '>= 20'} + + '@vercel/error-utils@2.2.1': + resolution: {integrity: sha512-9DhP8jP7raLML4hGsBemxX5fXuQnu5xxMV+HjGygGbzEmVK/+KyJ3QP2Cw7PdF0uXdb9N0Qa4c3tRGH34ZX6vw==} + + '@vercel/microfrontends@1.2.2': + resolution: {integrity: sha512-QzcR5wVsz654XlCGT/HnxjgSkQMpeaEuj7bI1EeukwL/2D3kFkL5J68LMqA68HHSbbLvx32o7n5scSoB7a+3IQ==} + hasBin: true + peerDependencies: + '@sveltejs/kit': '>=1' + '@vercel/analytics': '>=1.5.0' + '@vercel/speed-insights': '>=1.2.0' + next: '>=13' + react: '>=17.0.0' + react-dom: '>=17.0.0' + vite: ^7.3.5 + peerDependenciesMeta: + '@sveltejs/kit': + optional: true + '@vercel/analytics': + optional: true + '@vercel/speed-insights': + optional: true + next: + optional: true + react: + optional: true + react-dom: + optional: true + vite: + optional: true + + '@vercel/python-analysis@0.13.1': + resolution: {integrity: sha512-ec4tii9I7i6eHfvsvG/eaNaKh0TxmxBsc904V7yjwZImeyFTEVvDSLQ/+510FB+wBG6gCvpyqBH5aAKP9llqsw==} + + '@vercel/routing-utils@6.4.1': + resolution: {integrity: sha512-d/DqMAbxV8ymZdPzVbmihFj9oK3IvskucNbRN18ik53JLFtMlfu8v1SuVTt+iRhnYXaeA/y4NYLcyOnkkNmq/Q==} + '@vitest/expect@4.1.8': resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} @@ -1735,6 +1806,9 @@ packages: ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + ansi-regex@4.1.1: resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} engines: {node: '>=6'} @@ -1774,15 +1848,25 @@ packages: resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==} engines: {node: '>=4'} + async-retry@1.2.3: + resolution: {integrity: sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==} + async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + async-sema@3.0.0: + resolution: {integrity: sha512-zyCMBDl4m71feawrxYcVbHxv/UUkqm4nKJiLu3+l9lfiQha6jQ/9dxhrXLnzzBXVFqCTDwiUkZOz9XFbdEGQsg==} + async@3.2.2: resolution: {integrity: sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} @@ -1807,6 +1891,9 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bl@1.2.3: + resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -1829,9 +1916,18 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-alloc-unsafe@1.1.0: + resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==} + + buffer-alloc@1.2.0: + resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-fill@1.0.0: + resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1842,6 +1938,14 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1861,6 +1965,9 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -1872,6 +1979,9 @@ packages: resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} engines: {node: '>=8'} + cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -1910,12 +2020,23 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.4.0: + resolution: {integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1935,6 +2056,10 @@ packages: defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1979,6 +2104,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@1.5.0: + resolution: {integrity: sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==} + es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} @@ -2066,6 +2194,9 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -2079,12 +2210,19 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.5: + resolution: {integrity: sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==} + fast-xml-builder@1.2.0: resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} @@ -2096,6 +2234,9 @@ packages: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -2124,6 +2265,19 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -2139,6 +2293,17 @@ packages: forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + + fs-extra@8.0.1: + resolution: {integrity: sha512-W+XLrggcDzlle47X/XnS7FXrXu9sDo+Ze9zpndeBxdgv88FHLm1HtmkhEwavruS6koanBjp098rUpHs65EmG7A==} + engines: {node: '>=6 <7 || >=8'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2222,6 +2387,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + gtoken@7.1.0: resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} engines: {node: '>=14.0.0'} @@ -2230,6 +2398,9 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -2249,6 +2420,10 @@ packages: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -2260,6 +2435,10 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@4.0.6: + resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} + engines: {node: '>= 4'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2286,6 +2465,10 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + is-ci@2.0.0: resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} hasBin: true @@ -2317,10 +2500,20 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2349,6 +2542,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2360,6 +2556,15 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} @@ -2414,6 +2619,14 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2435,6 +2648,9 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@4.2.8: resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} engines: {node: '>=8'} @@ -2451,6 +2667,10 @@ packages: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -2459,6 +2679,9 @@ packages: module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2564,6 +2787,9 @@ packages: resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} engines: {node: 20 || >=22} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -2589,6 +2815,10 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postcss@8.5.24: resolution: {integrity: sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw==} engines: {node: ^10 || ^12 || >=14} @@ -2618,6 +2848,9 @@ packages: engines: {node: '>=14'} hasBin: true + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -2636,10 +2869,24 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pump@1.0.3: + resolution: {integrity: sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + querystring@0.2.1: + resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} + engines: {node: '>=0.4.x'} + deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2648,10 +2895,17 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-in-the-middle@8.0.1: resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2667,15 +2921,29 @@ packages: resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==} engines: {node: '>=14'} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rollup@4.61.1: resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -2688,6 +2956,10 @@ packages: engines: {node: '>=10'} hasBin: true + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2716,6 +2988,13 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + sleep-promise@8.0.1: + resolution: {integrity: sha512-nfwyX+G1dsx2R1DMMKWLpNxuHMOCL7JIRBUw0fl7Z4nZ1YZK0apZuGY8MDexn0HDZzgbERgj/CrNtsYpo/B7eA==} + + smol-toml@1.5.2: + resolution: {integrity: sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==} + engines: {node: '>= 18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2758,6 +3037,9 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -2787,6 +3069,13 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + tar-fs@1.16.6: + resolution: {integrity: sha512-JkOgFt3FxM/2v2CNpAVHqMW2QASjc/Hxo7IGfNd3MHaDYSW/sBFiS7YVmmhmr8x6vwN1VFQDQGdT2MWpmIuVKA==} + + tar-stream@1.6.2: + resolution: {integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==} + engines: {node: '>= 0.8.0'} + tar@7.5.19: resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==} engines: {node: '>=18'} @@ -2821,6 +3110,10 @@ packages: resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} engines: {node: '>=14.14'} + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2846,6 +3139,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + typescript-eslint@8.50.1: resolution: {integrity: sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2864,6 +3161,14 @@ packages: universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + unplugin@1.0.1: resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} @@ -2980,6 +3285,10 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3044,6 +3353,9 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} + zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -3545,6 +3857,8 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@bytecodealliance/preview2-shim@0.17.6': {} + '@esbuild/aix-ppc64@0.27.7': optional: true @@ -3828,8 +4142,22 @@ snapshots: '@kwsites/promise-deferred@1.1.1': {} + '@next/env@15.1.6': {} + '@nodable/entities@2.1.0': {} + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + '@octokit/auth-token@5.1.2': {} '@octokit/core@6.1.6': @@ -4147,6 +4475,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@renovatebot/pep440@4.2.1': {} + '@rollup/rollup-android-arm-eabi@4.61.1': optional: true @@ -4878,6 +5208,72 @@ snapshots: '@typescript-eslint/types': 8.50.1 eslint-visitor-keys: 4.2.1 + '@vercel/build-utils@14.0.1': + dependencies: + '@vercel/python-analysis': 0.13.1 + cjs-module-lexer: 1.2.3 + es-module-lexer: 1.5.0 + + '@vercel/client@18.2.5(vite@7.3.5(@types/node@24.13.2)(tsx@4.21.0))': + dependencies: + '@vercel/build-utils': 14.0.1 + '@vercel/error-utils': 2.2.1 + '@vercel/microfrontends': 1.2.2(vite@7.3.5(@types/node@24.13.2)(tsx@4.21.0)) + '@vercel/routing-utils': 6.4.1 + async-retry: 1.2.3 + async-sema: 3.0.0 + fs-extra: 8.0.1 + ignore: 4.0.6 + minimatch: 10.2.6 + ms: 2.1.2 + querystring: 0.2.1 + sleep-promise: 8.0.1 + tar-fs: 1.16.6 + transitivePeerDependencies: + - '@sveltejs/kit' + - '@vercel/analytics' + - '@vercel/speed-insights' + - debug + - next + - react + - react-dom + - vite + + '@vercel/error-utils@2.2.1': {} + + '@vercel/microfrontends@1.2.2(vite@7.3.5(@types/node@24.13.2)(tsx@4.21.0))': + dependencies: + '@next/env': 15.1.6 + ajv: 8.20.0 + commander: 12.1.0 + cookie: 0.4.0 + fast-glob: 3.3.3 + http-proxy: 1.18.1 + jsonc-parser: 3.3.1 + nanoid: 3.3.16 + path-to-regexp: 6.3.0 + optionalDependencies: + vite: 7.3.5(@types/node@24.13.2)(tsx@4.21.0) + transitivePeerDependencies: + - debug + + '@vercel/python-analysis@0.13.1': + dependencies: + '@bytecodealliance/preview2-shim': 0.17.6 + '@renovatebot/pep440': 4.2.1 + fs-extra: 11.1.1 + js-yaml: 4.3.0 + minimatch: 10.2.6 + smol-toml: 1.5.2 + zod: 3.22.4 + + '@vercel/routing-utils@6.4.1': + dependencies: + path-to-regexp: 6.3.0 + path-to-regexp-updated: path-to-regexp@6.3.0 + optionalDependencies: + ajv: 6.14.0 + '@vitest/expect@4.1.8': dependencies: '@standard-schema/spec': 1.1.0 @@ -4948,6 +5344,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.5 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@4.1.1: {} ansi-regex@5.0.1: {} @@ -4973,14 +5376,24 @@ snapshots: astral-regex@1.0.0: {} + async-retry@1.2.3: + dependencies: + retry: 0.12.0 + async-retry@1.3.3: dependencies: retry: 0.13.1 + async-sema@3.0.0: {} + async@3.2.2: {} asynckit@0.4.0: {} + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + aws4@1.13.2: {} balanced-match@4.0.4: {} @@ -4995,6 +5408,11 @@ snapshots: binary-extensions@2.3.0: {} + bl@1.2.3: + dependencies: + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -5021,8 +5439,17 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) + buffer-alloc-unsafe@1.1.0: {} + + buffer-alloc@1.2.0: + dependencies: + buffer-alloc-unsafe: 1.1.0 + buffer-fill: 1.0.0 + buffer-equal-constant-time@1.0.1: {} + buffer-fill@1.0.0: {} + buffer-from@1.1.2: {} buffer@5.7.1: @@ -5035,6 +5462,18 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + callsites@3.1.0: {} caniuse-lite@1.0.30001763: {} @@ -5058,12 +5497,16 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chownr@1.1.4: {} + chownr@3.0.0: {} ci-info@2.0.0: {} ci-info@4.3.1: {} + cjs-module-lexer@1.2.3: {} + cjs-module-lexer@1.4.3: {} cli-cursor@3.1.0: @@ -5096,10 +5539,16 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@12.1.0: {} + consola@2.15.3: {} convert-source-map@2.0.0: {} + cookie@0.4.0: {} + + core-util-is@1.0.3: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5116,6 +5565,12 @@ snapshots: dependencies: clone: 1.0.4 + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + delayed-stream@1.0.0: {} dotenv@16.6.1: {} @@ -5155,6 +5610,8 @@ snapshots: es-errors@1.3.0: {} + es-module-lexer@1.5.0: {} + es-module-lexer@2.1.0: {} es-object-atoms@1.1.1: @@ -5308,6 +5765,8 @@ snapshots: event-target-shim@5.0.1: {} + eventemitter3@4.0.7: {} + expect-type@1.3.0: {} extend@3.0.2: {} @@ -5316,10 +5775,20 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} + fast-uri@3.1.5: {} + fast-xml-builder@1.2.0: dependencies: path-expression-matcher: 1.5.0 @@ -5335,6 +5804,10 @@ snapshots: fastest-levenshtein@1.0.16: {} + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -5359,6 +5832,12 @@ snapshots: flatted@3.4.2: {} + follow-redirects@1.16.0: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -5383,6 +5862,20 @@ snapshots: forwarded-parse@2.1.2: {} + fs-constants@1.0.0: {} + + fs-extra@11.1.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs-extra@8.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -5490,6 +5983,8 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.11: {} + gtoken@7.1.0: dependencies: gaxios: 6.7.1 @@ -5500,6 +5995,10 @@ snapshots: has-flag@4.0.0: {} + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -5520,6 +6019,14 @@ snapshots: transitivePeerDependencies: - supports-color + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.16.0 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 @@ -5536,6 +6043,8 @@ snapshots: ieee754@1.2.1: {} + ignore@4.0.6: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -5560,6 +6069,8 @@ snapshots: dependencies: binary-extensions: 2.3.0 + is-callable@1.2.7: {} + is-ci@2.0.0: dependencies: ci-info: 2.0.0 @@ -5582,8 +6093,16 @@ snapshots: is-stream@2.0.1: {} + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + is-unicode-supported@0.1.0: {} + isarray@1.0.0: {} + + isarray@2.0.5: {} + isexe@2.0.0: {} jackspeak@4.1.1: @@ -5606,12 +6125,26 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json-stringify-safe@5.0.1: {} json5@2.2.3: {} + jsonc-parser@3.3.1: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + jwa@2.0.1: dependencies: buffer-equal-constant-time: 1.0.1 @@ -5665,6 +6198,13 @@ snapshots: math-intrinsics@1.1.0: {} + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + mime-db@1.52.0: {} mime-types@2.1.35: @@ -5679,6 +6219,8 @@ snapshots: dependencies: brace-expansion: 5.0.8 + minimist@1.2.8: {} + minipass@4.2.8: {} minipass@7.1.2: {} @@ -5689,10 +6231,16 @@ snapshots: dependencies: minipass: 7.1.3 + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + mkdirp@1.0.4: {} module-details-from-path@1.0.4: {} + ms@2.1.2: {} + ms@2.1.3: {} mustache@3.0.1: {} @@ -5793,6 +6341,8 @@ snapshots: lru-cache: 11.2.2 minipass: 7.1.2 + path-to-regexp@6.3.0: {} + pathe@2.0.3: {} pg-int8@1.0.1: {} @@ -5813,6 +6363,8 @@ snapshots: picomatch@4.0.4: {} + possible-typed-array-names@1.1.0: {} + postcss@8.5.24: dependencies: nanoid: 3.3.16 @@ -5833,6 +6385,8 @@ snapshots: prettier@3.7.4: {} + process-nextick-args@2.0.1: {} + progress@2.0.3: {} prompts@2.4.1: @@ -5846,8 +6400,27 @@ snapshots: proxy-from-env@1.1.0: {} + pump@1.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + punycode@2.3.1: {} + querystring@0.2.1: {} + + queue-microtask@1.2.3: {} + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -5858,6 +6431,8 @@ snapshots: dependencies: picomatch: 2.3.2 + require-from-string@2.0.2: {} + require-in-the-middle@8.0.1: dependencies: debug: 4.4.3 @@ -5865,6 +6440,8 @@ snapshots: transitivePeerDependencies: - supports-color + requires-port@1.0.0: {} + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: @@ -5884,8 +6461,12 @@ snapshots: - encoding - supports-color + retry@0.12.0: {} + retry@0.13.1: {} + reusify@1.1.0: {} + rollup@4.61.1: dependencies: '@types/estree': 1.0.9 @@ -5917,12 +6498,27 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.61.1 fsevents: 2.3.3 + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} semver@6.3.1: {} semver@7.7.3: {} + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -5949,6 +6545,10 @@ snapshots: sisteransi@1.0.5: {} + sleep-promise@8.0.1: {} + + smol-toml@1.5.2: {} + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -5995,6 +6595,10 @@ snapshots: get-east-asian-width: 1.4.0 strip-ansi: 7.1.2 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -6021,6 +6625,23 @@ snapshots: dependencies: has-flag: 4.0.0 + tar-fs@1.16.6: + dependencies: + chownr: 1.1.4 + mkdirp: 0.5.6 + pump: 1.0.3 + tar-stream: 1.6.2 + + tar-stream@1.6.2: + dependencies: + bl: 1.2.3 + buffer-alloc: 1.2.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + readable-stream: 2.3.8 + to-buffer: 1.2.2 + xtend: 4.0.2 + tar@7.5.19: dependencies: '@isaacs/fs-minipass': 4.0.1 @@ -6060,6 +6681,12 @@ snapshots: tmp@0.2.7: {} + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -6084,6 +6711,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + typescript-eslint@8.50.1(eslint@9.39.2)(typescript@5.9.3): dependencies: '@typescript-eslint/eslint-plugin': 8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) @@ -6101,6 +6734,10 @@ snapshots: universal-user-agent@7.0.3: {} + universalify@0.1.2: {} + + universalify@2.0.1: {} + unplugin@1.0.1: dependencies: acorn: 8.15.0 @@ -6178,6 +6815,16 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -6234,4 +6881,6 @@ snapshots: yocto-queue@1.2.2: {} + zod@3.22.4: {} + zod@3.25.76: {} diff --git a/src/targets/__tests__/vercel.test.ts b/src/targets/__tests__/vercel.test.ts new file mode 100644 index 00000000..59c34026 --- /dev/null +++ b/src/targets/__tests__/vercel.test.ts @@ -0,0 +1,319 @@ +import { vi } from 'vitest'; + +import { createDeployment } from '@vercel/client'; + +import { VercelTarget, targetSecrets } from '../vercel'; +import { NoneArtifactProvider } from '../../artifact_providers/none'; +import * as system from '../../utils/system'; +import { isDryRun } from '../../utils/helpers'; + +const TMP_DIR = '/tmp/craft-vercel-test'; +const DEFAULT_SECRET_VALUE = 'secret_value'; +const ORG_ID = 'org_1234'; +const PROJECT_ID = 'prj_1234'; + +vi.mock('../../utils/helpers'); + +vi.mock('@vercel/client', () => ({ + createDeployment: vi.fn(), +})); + +vi.mock('../../utils/system', async importOriginal => { + const actual = await importOriginal(); + return { + ...actual, + extractZipArchive: vi.fn(async () => undefined), + }; +}); + +vi.mock('../../utils/files', async importOriginal => { + const actual = await importOriginal(); + return { + ...actual, + withTempDir: async (cb: (dir: string) => Promise) => cb(TMP_DIR), + }; +}); + +function mockDeployment(url = 'my-app.vercel.app'): void { + (createDeployment as any).mockImplementation(async function* () { + yield { type: 'ready', payload: { url } }; + }); +} + +function mockDeploymentWithAlias(url = 'my-app.vercel.app'): void { + (createDeployment as any).mockImplementation(async function* () { + yield { type: 'ready', payload: { url } }; + yield { type: 'alias-assigned', payload: { url } }; + }); +} + +function mockDeploymentError(error: Error): void { + (createDeployment as any).mockImplementation(async function* () { + yield { type: 'error', payload: error }; + }); +} + +function setTargetSecretsInEnv(): void { + for (const secret of targetSecrets) { + process.env[secret] = DEFAULT_SECRET_VALUE; + } +} + +function removeTargetSecretsFromEnv(): void { + for (const secret of targetSecrets) { + delete process.env[secret]; + } +} + +function createVercelTarget( + targetConfig?: Record, +): VercelTarget { + return new VercelTarget( + { + name: 'vercel', + ...targetConfig, + }, + new NoneArtifactProvider(), + { owner: 'testOwner', repo: 'testRepo' }, + ); +} + +beforeEach(() => { + setTargetSecretsInEnv(); + delete process.env.VERCEL_ORG_ID; + delete process.env.VERCEL_PROJECT_ID; + (isDryRun as any).mockReturnValue(false); +}); + +afterEach(() => { + removeTargetSecretsFromEnv(); + delete process.env.VERCEL_ORG_ID; + delete process.env.VERCEL_PROJECT_ID; + vi.clearAllMocks(); +}); + +describe('vercel target configuration', () => { + test('exports the expected secrets (only the token is a secret)', () => { + expect(targetSecrets).toContain('VERCEL_TOKEN'); + expect(targetSecrets).not.toContain('VERCEL_ORG_ID'); + expect(targetSecrets).not.toContain('VERCEL_PROJECT_ID'); + }); + + test('enforces the required token secret', () => { + removeTargetSecretsFromEnv(); + + expect(() => createVercelTarget({})).toThrowErrorMatchingInlineSnapshot( + `[Error: Required value(s) VERCEL_TOKEN not found in configuration files or the environment. See the documentation for more details.]`, + ); + }); + + test('does not require VERCEL_ORG_ID or VERCEL_PROJECT_ID', () => { + expect(() => createVercelTarget({})).not.toThrow(); + }); + + test('applies default options', () => { + const target = createVercelTarget({}); + + expect(target.vercelConfig).toStrictEqual({ + VERCEL_TOKEN: DEFAULT_SECRET_VALUE, + prebuilt: true, + workingDir: undefined, + orgId: undefined, + projectId: undefined, + }); + }); + + test('picks up VERCEL_ORG_ID and VERCEL_PROJECT_ID from env when set', () => { + process.env.VERCEL_ORG_ID = ORG_ID; + process.env.VERCEL_PROJECT_ID = PROJECT_ID; + const target = createVercelTarget({}); + expect(target.vercelConfig.orgId).toBe(ORG_ID); + expect(target.vercelConfig.projectId).toBe(PROJECT_ID); + }); + + test('allows overriding default options', () => { + const target = createVercelTarget({ + prebuilt: false, + workingDir: 'subdir', + }); + + expect(target.vercelConfig).toStrictEqual({ + VERCEL_TOKEN: DEFAULT_SECRET_VALUE, + prebuilt: false, + workingDir: 'subdir', + orgId: undefined, + projectId: undefined, + }); + }); +}); + +describe('publish', () => { + const revision = 'deadbeef'; + const version = '1.2.3'; + const artifact = { filename: 'vercel.zip' } as any; + + function stubArtifacts(target: VercelTarget, artifacts: any[]): void { + target.getArtifactsForRevision = vi.fn(async () => artifacts); + target.artifactProvider.downloadArtifact = vi.fn( + async () => '/downloads/vercel.zip', + ); + } + + test('deploys a prebuilt artifact to production with provenance', async () => { + mockDeployment(); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + expect(system.extractZipArchive).toHaveBeenCalledWith( + '/downloads/vercel.zip', + TMP_DIR, + ); + + expect(createDeployment).toHaveBeenCalledTimes(1); + const [clientOptions, deploymentOptions] = (createDeployment as any).mock + .calls[0]; + expect(clientOptions.token).toBe(DEFAULT_SECRET_VALUE); + expect(clientOptions.path).toBe(TMP_DIR); + expect(clientOptions.prebuilt).toBe(true); + expect(clientOptions.skipAutoDetectionConfirmation).toBe(true); + expect(deploymentOptions.target).toBe('production'); + expect(deploymentOptions.meta.release).toBe(version); + }); + + test('omits prebuilt when prebuilt is false', async () => { + mockDeployment(); + const target = createVercelTarget({ prebuilt: false }); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + const [clientOptions] = (createDeployment as any).mock.calls[0]; + expect(clientOptions.prebuilt).toBe(false); + }); + + test('does not forward org/project IDs when unset', async () => { + mockDeployment(); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + const [clientOptions, deploymentOptions] = (createDeployment as any).mock + .calls[0]; + expect(clientOptions.teamId).toBeUndefined(); + expect('project' in deploymentOptions).toBe(false); + }); + + test('forwards org ID when set', async () => { + mockDeployment(); + process.env.VERCEL_ORG_ID = ORG_ID; + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + const [clientOptions] = (createDeployment as any).mock.calls[0]; + expect(clientOptions.teamId).toBe(ORG_ID); + }); + + test('links the deployment to the project when VERCEL_PROJECT_ID is set', async () => { + mockDeployment(); + process.env.VERCEL_PROJECT_ID = PROJECT_ID; + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + const [, deploymentOptions] = (createDeployment as any).mock.calls[0]; + expect(deploymentOptions.project).toBe(PROJECT_ID); + }); + + test('deploys from workingDir subdirectory when configured', async () => { + mockDeployment(); + const target = createVercelTarget({ workingDir: 'dist' }); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + const [clientOptions] = (createDeployment as any).mock.calls[0]; + expect(clientOptions.path).toBe(`${TMP_DIR}/dist`); + }); + + test('reports an error and does not deploy when no artifacts found', async () => { + mockDeployment(); + const target = createVercelTarget({}); + stubArtifacts(target, []); + + await expect(target.publish(version, revision)).rejects.toThrow( + /no artifacts found/, + ); + expect(createDeployment).not.toHaveBeenCalled(); + }); + + test('reports an error when more than one artifact found', async () => { + mockDeployment(); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact, artifact]); + + await expect(target.publish(version, revision)).rejects.toThrow( + /more than one Vercel archive/, + ); + expect(createDeployment).not.toHaveBeenCalled(); + }); + + test('does not deploy in dry-run mode (including worktree mode)', async () => { + (isDryRun as any).mockReturnValue(true); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + // Artifact is still extracted (local, safe), but the remote deploy is + // skipped. + expect(system.extractZipArchive).toHaveBeenCalled(); + expect(createDeployment).not.toHaveBeenCalled(); + }); + + test('throws when the deploy stream yields an error event', async () => { + mockDeploymentError(new Error('boom')); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await expect(target.publish(version, revision)).rejects.toThrow(/boom/); + }); + + test('succeeds when the deploy stream yields alias-assigned after ready', async () => { + mockDeploymentWithAlias(); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + expect(createDeployment).toHaveBeenCalledTimes(1); + }); + + test('succeeds with a ready deployment when no alias-assigned event arrives', async () => { + mockDeployment(); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await target.publish(version, revision); + + expect(createDeployment).toHaveBeenCalledTimes(1); + }); + + test('throws when the stream ends without a ready deployment', async () => { + (createDeployment as any).mockImplementation(async function* () { + // Stream ends without ready/alias-assigned/error events. + yield { type: 'created', payload: { url: 'x.vercel.app' } }; + }); + const target = createVercelTarget({}); + stubArtifacts(target, [artifact]); + + await expect(target.publish(version, revision)).rejects.toThrow( + /without a ready deployment/, + ); + }); +}); diff --git a/src/targets/index.ts b/src/targets/index.ts index ff51af27..2b2053ed 100644 --- a/src/targets/index.ts +++ b/src/targets/index.ts @@ -21,6 +21,7 @@ import { PubDevTarget } from './pubDev'; import { HexTarget } from './hex'; import { CommitOnGitRepositoryTarget } from './commitOnGitRepository'; import { PowerShellTarget } from './powershell'; +import { VercelTarget } from './vercel'; export const TARGET_MAP: { [key: string]: typeof BaseTarget } = { brew: BrewTarget, @@ -45,6 +46,7 @@ export const TARGET_MAP: { [key: string]: typeof BaseTarget } = { hex: HexTarget, 'commit-on-git-repository': CommitOnGitRepositoryTarget, powershell: PowerShellTarget, + vercel: VercelTarget, }; /** Targets that are treated specially */ diff --git a/src/targets/vercel.ts b/src/targets/vercel.ts new file mode 100644 index 00000000..f51dae61 --- /dev/null +++ b/src/targets/vercel.ts @@ -0,0 +1,256 @@ +import { join } from 'path'; + +import { createDeployment } from '@vercel/client'; + +import { + GitHubGlobalConfig, + TargetConfig, + TypedTargetConfig, +} from '../schemas/project_config'; +import { checkEnvForPrerequisite } from '../utils/env'; +import { reportError } from '../utils/errors'; +import { withTempDir } from '../utils/files'; +import { isDryRun } from '../utils/helpers'; +import { logDryRun } from '../utils/dryRun'; +import { extractZipArchive } from '../utils/system'; +import { BaseTarget } from './base'; +import { BaseArtifactProvider } from '../artifact_providers/base'; + +/** + * Secrets required to authenticate with the Vercel API. + * + * Only the token is a true secret. The org/team and project IDs are + * identifiers, not credentials, and are handled separately (see the + * `*_ID_ENV_VAR` constants below): they are optional and, when set, forwarded + * to the deploy call so it links to the right project non-interactively. + * + * Exported so tests (and documentation tooling) can reference the canonical + * list of environment variables this target consumes. + */ +export const targetSecrets = ['VERCEL_TOKEN'] as const; +type SecretsType = (typeof targetSecrets)[number]; + +/** + * Optional, non-secret identifiers forwarded to the Vercel API when present. + * They link the deployment to a specific team/project non-interactively, which + * is what CI needs. Without a project ID, the deployment name is derived from + * the deploy directory, which would not reliably target the intended project. + */ +const ORG_ID_ENV_VAR = 'VERCEL_ORG_ID'; +const PROJECT_ID_ENV_VAR = 'VERCEL_PROJECT_ID'; + +/** + * Regex for the Vercel deploy archive. + * + * Matches e.g. `vercel.zip` or `my-docs-vercel.zip`. Can be overridden via the + * `includeNames` target option. + */ +const DEFAULT_DEPLOY_ARCHIVE_REGEX = /^(?:.+-)?vercel\.zip$/; + +/** Fields on the vercel target config accessed at runtime */ +interface VercelConfigFields extends Record { + prebuilt?: boolean; + workingDir?: string; +} + +/** Target options for "vercel" */ +export interface VercelTargetConfig { + /** + * Whether the artifact contains a prebuilt `.vercel/output` (the result of + * `vercel build`). When true, the deploy is created with `prebuilt` and skips + * the remote build step. Defaults to true: the docs website is built in CI + * and the release just promotes the prebuilt output to production. + */ + prebuilt: boolean; + /** Subdirectory within the extracted artifact to deploy from */ + workingDir?: string; + /** + * Optional Vercel org/team ID (an identifier, not a secret). Forwarded to the + * deploy as `teamId` when set. + */ + orgId?: string; + /** + * Optional Vercel project ID (an identifier, not a secret). Forwarded to the + * deploy as the `project` identifier so the deployment links to the intended + * project non-interactively. + */ + projectId?: string; +} + +/** + * Full config for the "vercel" target, including secrets. + */ +export type VercelTargetFullConfig = VercelTargetConfig & + Record; + +/** + * Target responsible for deploying a prebuilt static site to Vercel. + * + * Uses the Vercel deploy API (via `@vercel/client`) to promote a release + * artifact to production. Intended for release-gated documentation sites: the + * artifact is built in CI and this target only publishes it, keeping the docs + * in sync with the released version. + */ +export class VercelTarget extends BaseTarget { + /** Target name */ + public readonly name: string = 'vercel'; + /** Target options */ + public readonly vercelConfig: VercelTargetFullConfig; + /** GitHub repo configuration */ + public readonly githubRepo: GitHubGlobalConfig; + + public constructor( + config: TargetConfig, + artifactProvider: BaseArtifactProvider, + githubRepo: GitHubGlobalConfig, + ) { + super(config, artifactProvider, githubRepo); + this.githubRepo = githubRepo; + this.vercelConfig = this.getVercelConfig(); + } + + /** + * Extracts, validates and returns the "vercel" target options. + * + * @returns the vercel config for this target. + */ + public getVercelConfig(): VercelTargetFullConfig { + const config = this.config as TypedTargetConfig; + + return { + prebuilt: config.prebuilt ?? true, + workingDir: config.workingDir, + // Optional, non-secret identifiers. Forwarded to the deploy when present. + orgId: process.env[ORG_ID_ENV_VAR] || undefined, + projectId: process.env[PROJECT_ID_ENV_VAR] || undefined, + ...this.getTargetSecrets(), + }; + } + + private getTargetSecrets(): Record { + return targetSecrets + .map(name => { + checkEnvForPrerequisite({ name }); + return { + name, + value: process.env[name] as string, + }; + }) + .reduce( + (prev, current) => ({ + ...prev, + [current.name]: current.value, + }), + {}, + ) as Record; + } + + /** + * Runs a Vercel production deploy of `deployDir` and waits for it to finish. + * + * Drives the `@vercel/client` event stream to completion: resolves on the + * `alias-assigned` event (the production promotion is done), falls back to + * the `ready` deployment URL when the stream ends without an alias event, and + * throws on the `error` event so a failed deploy fails the release. + * + * @param deployDir Directory to deploy from + * @param version The version being released (attached as deploy provenance) + * @returns the production deployment URL + */ + private async deploy(deployDir: string, version: string): Promise { + let url: string | undefined; + const deploymentOptions: Record = { + // `production` deploys to production; `release` ties the deployment + // back to the released version for traceability. + target: 'production', + meta: { release: version }, + }; + if (this.vercelConfig.projectId) { + // `project` (the project ID) overrides `name` and reliably links the + // deployment to the configured project non-interactively. + deploymentOptions.project = this.vercelConfig.projectId; + } + for await (const event of createDeployment( + { + token: this.vercelConfig.VERCEL_TOKEN, + path: deployDir, + prebuilt: this.vercelConfig.prebuilt, + teamId: this.vercelConfig.orgId, + skipAutoDetectionConfirmation: true, + }, + deploymentOptions, + )) { + if (event.type === 'ready') { + url = event.payload.url as string; + } + if (event.type === 'alias-assigned') { + return url || (event.payload.url as string); + } + if (event.type === 'error') { + throw event.payload; + } + } + if (url) { + // Production alias assignment is best-effort for some projects; if the + // stream ended after `ready` we still return the URL. + return url; + } + throw new Error('Vercel deploy finished without a ready deployment'); + } + + /** + * Deploys the release artifact to Vercel via the Vercel deploy API. + * + * @param version New version to be released + * @param revision Git commit SHA to be published + */ + public async publish(version: string, revision: string): Promise { + this.logger.debug('Fetching artifact list...'); + const packageFiles = await this.getArtifactsForRevision(revision, { + includeNames: DEFAULT_DEPLOY_ARCHIVE_REGEX, + }); + if (!packageFiles.length) { + reportError('Cannot deploy to Vercel: no artifacts found'); + return; + } else if (packageFiles.length > 1) { + reportError( + `Not implemented: more than one Vercel archive found\nDetails: ${JSON.stringify( + packageFiles, + )}`, + ); + return; + } + + const archivePath = await this.artifactProvider.downloadArtifact( + packageFiles[0], + ); + + await withTempDir( + async directory => { + this.logger.info(`Extracting "${archivePath}" to "${directory}"...`); + await extractZipArchive(archivePath, directory); + + const deployDir = this.vercelConfig.workingDir + ? join(directory, this.vercelConfig.workingDir) + : directory; + + // A Vercel deploy is a remote, irreversible operation with no local + // isolation. Unlike git/fs operations, it must NEVER run in dry-run + // mode -- including worktree mode. Guard explicitly here, before any + // network calls. + if (isDryRun()) { + logDryRun(`@vercel/client createDeployment (${deployDir})`); + return; + } + + this.logger.info('Deploying to Vercel...'); + const url = await this.deploy(deployDir, version); + this.logger.info(`Vercel deploy live at https://${url}`); + }, + true, + 'craft-vercel-', + ); + + this.logger.info('Vercel deploy complete'); + } +}