diff --git a/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost/AppHost.cs b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost/AppHost.cs index 98e710107..8a4b5bc9b 100644 --- a/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost/AppHost.cs +++ b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost/AppHost.cs @@ -23,4 +23,13 @@ .WithMappedEndpointPort() .WithHttpHealthCheck(); +var vp = builder.AddVitePlusApp("vp-demo") + .WithNpm(install: true) + .WithPackageManagerLaunch(); + +vp.AddApp("vp-web", "apps/website") + .WithHttpEndpoint(env: "PORT") + .WithMappedEndpointPort() + .WithHttpHealthCheck(); + builder.Build().Run(); diff --git a/examples/javascript-ext/vp-demo/.gitignore b/examples/javascript-ext/vp-demo/.gitignore new file mode 100644 index 000000000..9e0f87e8e --- /dev/null +++ b/examples/javascript-ext/vp-demo/.gitignore @@ -0,0 +1,36 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# dotenv environment variable files +.env +.env.* +!.env.example + +# Editor directories and files +.vscode/* +!.vscode/settings.json +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# AI agent worktrees +.claude/worktrees/ +!.vscode/ +!.vscode/settings.json +!.vscode/extensions.json diff --git a/examples/javascript-ext/vp-demo/.vscode/extensions.json b/examples/javascript-ext/vp-demo/.vscode/extensions.json new file mode 100644 index 000000000..cd4f5c182 --- /dev/null +++ b/examples/javascript-ext/vp-demo/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["VoidZero.vite-plus-extension-pack"] +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/.vscode/settings.json b/examples/javascript-ext/vp-demo/.vscode/settings.json new file mode 100644 index 000000000..0950519cd --- /dev/null +++ b/examples/javascript-ext/vp-demo/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "npm.scriptRunner": "vp", + "editor.defaultFormatter": "oxc.oxc-vscode", + "[javascript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "oxc.disableNestedConfig": true, + "oxc.fmt.disableNestedConfig": true, + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.codeActionsOnSave": { + "source.fixAll.oxc": "explicit" + } +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/README.md b/examples/javascript-ext/vp-demo/README.md new file mode 100644 index 000000000..f9bae5210 --- /dev/null +++ b/examples/javascript-ext/vp-demo/README.md @@ -0,0 +1,29 @@ +# Vite+ Monorepo Starter + +A starter for creating a Vite+ monorepo. + +## Development + +- Check everything is ready: + +```bash +vp run ready +``` + +- Run the tests: + +```bash +vp run -r test +``` + +- Build the monorepo: + +```bash +vp run -r build +``` + +- Run the development server: + +```bash +vp run dev +``` \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/apps/website/.gitignore b/examples/javascript-ext/vp-demo/apps/website/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/javascript-ext/vp-demo/apps/website/index.html b/examples/javascript-ext/vp-demo/apps/website/index.html new file mode 100644 index 000000000..6ea677b1f --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/index.html @@ -0,0 +1,13 @@ + + + + + + + website + + +
+ + + \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/apps/website/package.json b/examples/javascript-ext/vp-demo/apps/website/package.json new file mode 100644 index 000000000..eee774a10 --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/package.json @@ -0,0 +1,15 @@ +{ + "name": "website", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vp dev", + "build": "tsc && vp build", + "preview": "vp preview" + }, + "devDependencies": { + "typescript": "^7.0.2", + "vite-plus": "1.3.0" + } +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/apps/website/public/favicon.svg b/examples/javascript-ext/vp-demo/apps/website/public/favicon.svg new file mode 100644 index 000000000..6893eb132 --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/apps/website/public/icons.svg b/examples/javascript-ext/vp-demo/apps/website/public/icons.svg new file mode 100644 index 000000000..e9522193d --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/javascript-ext/vp-demo/apps/website/src/assets/hero.png b/examples/javascript-ext/vp-demo/apps/website/src/assets/hero.png new file mode 100644 index 000000000..02251f4b9 Binary files /dev/null and b/examples/javascript-ext/vp-demo/apps/website/src/assets/hero.png differ diff --git a/examples/javascript-ext/vp-demo/apps/website/src/assets/typescript.svg b/examples/javascript-ext/vp-demo/apps/website/src/assets/typescript.svg new file mode 100644 index 000000000..6c9d69ce4 --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/src/assets/typescript.svg @@ -0,0 +1 @@ + diff --git a/examples/javascript-ext/vp-demo/apps/website/src/assets/vite.svg b/examples/javascript-ext/vp-demo/apps/website/src/assets/vite.svg new file mode 100644 index 000000000..5101b674d --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/examples/javascript-ext/vp-demo/apps/website/src/counter.ts b/examples/javascript-ext/vp-demo/apps/website/src/counter.ts new file mode 100644 index 000000000..e2fc51631 --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/src/counter.ts @@ -0,0 +1,9 @@ +export function setupCounter(element: HTMLButtonElement) { + let counter = 0; + const setCounter = (count: number) => { + counter = count; + element.innerHTML = `Count is ${counter}`; + }; + element.addEventListener("click", () => setCounter(counter + 1)); + setCounter(0); +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/apps/website/src/main.ts b/examples/javascript-ext/vp-demo/apps/website/src/main.ts new file mode 100644 index 000000000..961d17ec8 --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/src/main.ts @@ -0,0 +1,60 @@ +import "./style.css"; +import heroImg from "./assets/hero.png"; +import typescriptLogo from "./assets/typescript.svg"; +import viteLogo from "./assets/vite.svg"; +import { setupCounter } from "./counter.ts"; + +document.querySelector("#app")!.innerHTML = ` +
+
+ + TypeScript logo + Vite logo +
+
+

Get started

+

Edit src/main.ts and save to test HMR

+
+ +
+ +
+ +
+
+ +

Documentation

+

Your questions, answered

+ +
+
+ +

Connect with us

+

Join the Vite community

+ +
+
+ +
+
+`; + +setupCounter(document.querySelector("#counter")!); \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/apps/website/src/style.css b/examples/javascript-ext/vp-demo/apps/website/src/style.css new file mode 100644 index 000000000..63c1ad533 --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/src/style.css @@ -0,0 +1,292 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, "Segoe UI", Roboto, sans-serif; + --heading: system-ui, "Segoe UI", Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } +} + +body { + margin: 0; +} + +h1, +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h1 { + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } +} +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +code, +.counter { + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); +} + +code { + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); +} + +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) scale(0.8); + } +} + +#app { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ""; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/apps/website/tsconfig.json b/examples/javascript-ext/vp-demo/apps/website/tsconfig.json new file mode 100644 index 000000000..e945e379f --- /dev/null +++ b/examples/javascript-ext/vp-demo/apps/website/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2023", + "module": "esnext", + "lib": ["ES2023", "DOM"], + "types": ["vite/client"], + "allowArbitraryExtensions": true, + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/package-lock.json b/examples/javascript-ext/vp-demo/package-lock.json new file mode 100644 index 000000000..15fd763b7 --- /dev/null +++ b/examples/javascript-ext/vp-demo/package-lock.json @@ -0,0 +1,3188 @@ +{ + "name": "vp-demo", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vp-demo", + "version": "0.0.0", + "workspaces": [ + "packages/*", + "apps/*", + "tools/*" + ], + "devDependencies": { + "vite-plus": "1.3.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "apps/website": { + "version": "0.0.0", + "devDependencies": { + "typescript": "^7.0.2", + "vite-plus": "1.3.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", + "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@blazediff/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.10.0.tgz", + "integrity": "sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@oxc-project/runtime": { + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.154.0.tgz", + "integrity": "sha512-cBQNGoh659BNgvA8OC/e4BNDtFkv03dJaYdkp2adgOQNtISNAPQ+nB4S8XvWtNk7Xl+0g+7aa+iRGR25Xycd1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.154.0.tgz", + "integrity": "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@oxfmt/binding-android-arm-eabi": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.73.0.tgz", + "integrity": "sha512-dhunAdMbi/4o0Y66/+Wl6rpsNqpvE4i26Qw6bB77A97L2lJoktWg6m+X9QOon8iOEEqzD9rEZqFi5uZ4oIzhCA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-android-arm64": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.73.0.tgz", + "integrity": "sha512-dBoypjC2/xeyKyIhN2waX0/XeoccH9k0lVVe+sGRc4X8HSJVRtqyZDoXdr7bGY9A75c51dX0z0dLDpGQKwCdCg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-arm64": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.73.0.tgz", + "integrity": "sha512-Tn7Y+qWIpCQK+qdYwBcn6YvqeBuPiuG3tcNlYg16a5QL2o3rSmrgBB1nCyYqo0uvc1LNrfQW9/CK2tzFuID/Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-x64": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.73.0.tgz", + "integrity": "sha512-yVb0E0tg/+9Xc9YJCBq66qF1DC/aN5mhb5nnHz0k4ikwkMANtSul2VPDSwjYdXR6/DFimc+pPUgLBqkIa5Ctuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-freebsd-x64": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.73.0.tgz", + "integrity": "sha512-IKR4lFnBkYNL9Uqdxa5GMTQLvnLj+3sDwTvh2atHCzah0xeo+1xR5bigRU8uyXlOsA4VpXUn0UAimq4E4RBGmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.73.0.tgz", + "integrity": "sha512-7qnZ4g0ZCO3aZevOOQpZIABYwwXJW1lLDo2elviGdPQ0i11255xKEMFsi97fC1/nauQFseCJfELUy/k2JoOP+g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-musleabihf": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.73.0.tgz", + "integrity": "sha512-36eBico2j/WPB84gb4FYd/jjqGCKOwU4M+TWLBUIqlnzTozQueLy0joA5BwsSp2OZFVkOlpCwvA3ndPamdiyLw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-gnu": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.73.0.tgz", + "integrity": "sha512-VZ3cSgCAT18knKo52DcACmAuIoqG6qVEyeZRFbJd0xmYQ8GyYjuBc6IAh6MZHijT6q7p4/HPI+6ZWsCuhdV6iw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-musl": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.73.0.tgz", + "integrity": "sha512-8YVzH59XSHcqwXNgc5qK851zCqwpmBgOifARXEPPe5/xTsIbHceYoPZAyqfpYV0CfKmzSaEDBgYHFza4rhx+Zw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-ppc64-gnu": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.73.0.tgz", + "integrity": "sha512-MqRJnmCys0rwOgpWyEidSA7v/bvOqfNDJhPkQoR9mPVYrvSmMaW2Xhdpl28cbTA+brOM1is62GjIvJplVlUVpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-gnu": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.73.0.tgz", + "integrity": "sha512-4t2F3SWJSK/O/kRRFnL0elJzn75fJGGu71gs1HtiWcS80SmJOpkHiwwLnTp9suGdns8VHEPxYS0GTcyVwHKWBA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-musl": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.73.0.tgz", + "integrity": "sha512-D8b4I8I1kLdJQFwdFXNV+cwh3xWSiNKofjdGUk0nDpc0AHOds8T+poGbP9RWQMvgsvMb9OMgp/aV7ahBR37TbQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-s390x-gnu": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.73.0.tgz", + "integrity": "sha512-QrC1S5VL1+N5rvorB15jH7CPvjVHA7GyUI5oVEd8Bd9m5605RDBQYTxDQ920DuylRoZOh2V7/xXpfCtFUrojUQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-gnu": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.73.0.tgz", + "integrity": "sha512-dFcUKQPltCEPa+YlkF3zqufn6nJPZdG73LpnjBvZFFsU3laCMssDXSYB99mVaj43FqdEgjpz5TO6gbYXcczd9g==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-musl": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.73.0.tgz", + "integrity": "sha512-LmQe+nV8e+eUwfPNCxDF+owbsg88toBiR/8rZRgVOeKQP3MaGefXZRvBoc1UG37iWEQ/9JlCXG9Mc4nf4N3agw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-openharmony-arm64": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.73.0.tgz", + "integrity": "sha512-8weOr1OKknE0G2DbR6ejJUT9AOsoBU6cXlsetmbihHv1K+bK7qP61T6lJxY0k/aAO7GyOJiRGNlL+04r4eH8Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-arm64-msvc": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.73.0.tgz", + "integrity": "sha512-mZV+QxXYgb8E18gzAFItb937VIsE4dqWdStrD3tRRC52UubOYEuU+e7NaIqUsGdB5YsJqNeuck3Ue/af2r9wyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-ia32-msvc": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.73.0.tgz", + "integrity": "sha512-QJPUsbgUb97ZPPMkFUq6VQq0fLhC4Np19KL9aB/qKVwXnpKE6MIiILXqzhKnQ/UbnEDjKJ7Xv1d6zH4bYPCUdQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-x64-msvc": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.73.0.tgz", + "integrity": "sha512-cGcze2fu+ykIaFtkEvZlkZ3HvnCLk+WISthtq99zWTWn0x13t5L9TxvGdLZMUMvtfYR+ICkiCwKnguVubdcw0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint-tsgolint/darwin-arm64": { + "version": "7.0.2004", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-7.0.2004.tgz", + "integrity": "sha512-uJk3ttju7Lz+Nv8WG4CsLyewoL1qf/Ny8S8OksJ7Bm/F14Tc9gMfW6arM1PCc57IzH/X/I79YsCWTgxX1FKibQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint-tsgolint/darwin-x64": { + "version": "7.0.2004", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-x64/-/darwin-x64-7.0.2004.tgz", + "integrity": "sha512-0BFfHTzDxNDFPcu0UZAXT6CyCJHAXM0dZveyDb4KEHa2qF/DSnOKOrmvK/wFuFol9kvijbGM9JfuOsor+zWXRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint-tsgolint/linux-arm64": { + "version": "7.0.2004", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-arm64/-/linux-arm64-7.0.2004.tgz", + "integrity": "sha512-TAehQc+qdRXyDN3nl/r/3/oPrBVrbtrDeGMkFyG6mf+XphpoJ2lAMVFLclyvrVZCw02RbKROjyw8h72XwQQHcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint-tsgolint/linux-x64": { + "version": "7.0.2004", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-x64/-/linux-x64-7.0.2004.tgz", + "integrity": "sha512-LPunZvm6ZbWPfHMAyfZf9ci3HHvNEzLejO8zdqO3GoVGoSEHqqufHUD7+OgeatVSaJP7uXDJ2HaNOHIoH7go4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint-tsgolint/win32-arm64": { + "version": "7.0.2004", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-arm64/-/win32-arm64-7.0.2004.tgz", + "integrity": "sha512-FVWplzDImWhiuawnIZVETaQecv4GWom6Jv4Fx2xj7hhXXDZ5okEgiw1gTCImHTIQsi2ycC5oSxVwrn69bMh7iw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint-tsgolint/win32-x64": { + "version": "7.0.2004", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-x64/-/win32-x64-7.0.2004.tgz", + "integrity": "sha512-7PsxuUx11Ry9DYTWcljTzRbmZY9B6O9fFFfZV+aodQEMy6pfRT3fhoQRv9PtGN/89z1pPz3nFKrsFm6WqNtX7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.88.0.tgz", + "integrity": "sha512-k6Fgkf86ubfeH4GvqYsRolfuVAp0pfYVZvifSlXNHPdMaWUOfxUrmLEeDD7hF6U2oJuhsVxhSWq86/xCJ+9MmA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.88.0.tgz", + "integrity": "sha512-AsIyhl9o1ejwtac7NMbhKpMdSml6NWR0ljKjZERxC8OHYDAW8o/Om9n1QHVLi4VCEUo42Zte2/Op9kyoGpJZMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.88.0.tgz", + "integrity": "sha512-qgBjB7eEKcCB4RqjXGuoSZq74g0wD09fWFJ+HWcCJcKaga8TeUHtb+BY0SsxdleSLBgx4RZ7qmgo0EzqEq7JoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.88.0.tgz", + "integrity": "sha512-sCz3X5Mw/xdzEzn4fsCt1Iq8NKJnGNfhR+b9B5k+4DysYAHsmIhYoocnBPiQ9kvY3+pUun2A0Z8Vtun/Nv6YoQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.88.0.tgz", + "integrity": "sha512-xTOdYOIqCufgIj0knvxu86FBfovP6QBiD7ngNva01gb8xPQCD5gXkE5APGczH35zStim1O3zqvFbLQmY85xXRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.88.0.tgz", + "integrity": "sha512-BLadtw88NdYJBrAtC1dYzrPfSP2d5zgx6iuZycdD0YmZBJ2Nqbit9Z4PSaIb6gxK25dweArGMPbOmq/0ugwUPg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.88.0.tgz", + "integrity": "sha512-wILSczl/DaZsCcf7j7tvNbssp8hxEHIJxrv4j7+JjI3TAL1U/JTWbeUtoJJC/vo8+2hkTmHLYoKJjmiNbijdXw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.88.0.tgz", + "integrity": "sha512-jutT2Hy8JCuZuMwIFHo2HtyxfOfBJHyQxlBHBK1SL9W2f8n/WhjAP0FnDMz9d/mjhv9+QA0mx5AdQ9fDjsrFFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.88.0.tgz", + "integrity": "sha512-8N8iGV9hh4Lkeuq7QsEkjoXNRRflqR1G2qyoGYXcA0k5qllrdqmfylKEXyHIVERxqNDyMO4StqDQ0uLxXcBQAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.88.0.tgz", + "integrity": "sha512-5ydcQt3znNQ/rGEWYRvpS2cK9+T18P2VYgnyJ+eloEAwi9YUmsXy5VGrRqNWIK6qzWwHGnEE3ojVqqQNo+f0hA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.88.0.tgz", + "integrity": "sha512-iGx0uvFT54b/5DPJdEBCxYIrVB3v0uTy+JhRh3CyEp8/22BXDkG8rffkFwREdLsdj9mOrPbJYVOZw8ipcBuzjw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.88.0.tgz", + "integrity": "sha512-Wt47mKMWelVjRjuR7BCFxXkB0m79K1sTEbqMHRWciKUgo5iv7sWsF4RXSOyFYHD9zerNrkRZblnN273rhwOA1Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.88.0.tgz", + "integrity": "sha512-g3L12KCS4+1gtUt3RbJ4F+Yxx4LNmmZLEPfhd8rAHEJjJk2lAgNfnjGoP/phJyhG5WS6VbNYbJtadJelNfvTMA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.88.0.tgz", + "integrity": "sha512-og/XHVDOD9ZJgRiZkT9DgzQd4sr+JZW7e4l17Q/jVMZiIbeEd4uIu9iAoUbTF1gIvVZoRc4QDOxyVUvQGp4WHg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.88.0.tgz", + "integrity": "sha512-EmhBsp+xuKb6YbZNEMLBo80Xw71AZOIm18do8ni/GLX/Lw1XzNhVYpbxO1KasYtoAvrGtCOdCPULRlIE0bPbTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.88.0.tgz", + "integrity": "sha512-DBUcx/KwYdg1DAz+G01P/GFv512J04EsZadbHptx2FTpWT/MWRg21ZToNHt9pnsfQRBQ3et/g+vqzbvrdAVbuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.88.0.tgz", + "integrity": "sha512-xeVd0n4XG5HvynxfYvXi6uB+JvZbVZataowPS8cYpMbYg9mwE4sSyElFQd4LVJFifq8bRLDSR18XgEildmcXAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.88.0.tgz", + "integrity": "sha512-MA6iOw//amleaz2mVMsZrA7NHlEexjTrhIxSMFCXQi6IUuQO+3Od00GZhr2iCYw6RFzMW36jkKK/WHBXMsoQAw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.88.0.tgz", + "integrity": "sha512-3Sz6rVWe8Dy+ufsXurFN1GX2dX43F/lQMSLelK9gFjiba7nHvLlqlhR5cHlWVnCpSqUY1hN9pyCX9XToe6ve6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/plugins": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@oxlint/plugins/-/plugins-1.88.0.tgz", + "integrity": "sha512-G2xbfWTrbqYRhR/vfeKNMQf/sS3/ti0iKJAAkRxHuXWZH/hne/dyIb9QKk5LuR6cJLARxsL6FMoAYmfspd74MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@quansync/fs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-1.1.0.tgz", + "integrity": "sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz", + "integrity": "sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.8", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.8.tgz", + "integrity": "sha512-vgSSqnANMnkcYR/vUNDbwS7aM3OErRLNUG8vCVVlj4oc6cKffUrccX7UAS5AmJQQNZTSibi9z5GvvRZ0aYgUCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz", + "integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@vitest/browser": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-5.0.5.tgz", + "integrity": "sha512-IDMoZX/iZ8rkilw7weaSgN8tJ7iZ9hUv0wYoKXyiyjWjWFwLSWAY707DcVTgxTYOudWC1E3HA9LYn7kVa9a7uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@blazediff/core": "1.10.0", + "@vitest/mocker": "5.0.5", + "@vitest/ui": "5.0.5", + "@vitest/utils": "5.0.5", + "magic-string": "^1.4.3", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.2.0", + "ws": "^8.22.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "5.0.5" + } + }, + "node_modules/@vitest/browser-preview": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/browser-preview/-/browser-preview-5.0.5.tgz", + "integrity": "sha512-ViNg4UlomGSJHtnnr7Txm+55P7/aAMMoS+1WYnWjp5w5tzf7YEUAICcs2sb1uMovEnC8el/8zUfkWZ8+7fPCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "^10.4.2", + "@testing-library/user-event": "^14.6.7", + "@vitest/browser": "5.0.5" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "5.0.5" + } + }, + "node_modules/@vitest/mocker": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz", + "integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^1.4.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9 || ^3.0.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-5.0.5.tgz", + "integrity": "sha512-24sSdIlMA9DVPXnYiB9siwU0bTd887Y9jX3+vabztCSsvHwo7ipvJ/a2L3Rljw8+2rK1f5fCj3h5U9I/Jsqgbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-5.0.5.tgz", + "integrity": "sha512-dHyFElWMQy0wFBGrIaPPeUWfJFh+jAUrv46ImIJGd8CADdKV3mlrAu3FyD0wesdxELFCs9M807e040Kba/XKtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "5.0.5", + "@vitest/utils": "5.0.5", + "magic-string": "^1.4.3", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz", + "integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-5.0.5.tgz", + "integrity": "sha512-dCoGUOdgmCaSB2g+WQ/spPCBkytAWdNUkFSXY4z+eYp2d8JEOY8v0uDRaJTG9qf/kTlTVZ4CuYMYMz/f1HS/qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "5.0.5", + "fflate": "^0.8.3", + "flatted": "^3.4.4", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyrainbow": "^3.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "5.0.5" + } + }, + "node_modules/@vitest/utils": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-5.0.5.tgz", + "integrity": "sha512-/rBu7gzLDQI/SC79Tn98b5DwOgaHU/Gt4I9OVeP3YofzNaYtbImRCOJQmT1S6vOJg9gEIlWC+r3QxKkjuSa1nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "5.0.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@voidzero-dev/vite-plus-darwin-arm64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-darwin-arm64/-/vite-plus-darwin-arm64-1.3.0.tgz", + "integrity": "sha512-BF4W+UwGxUkHPrH2pZLhafJfkJT20WZIbMzZgZLGQ44LPpZPX8Sb+qtblUnXUkkCzTqad4XtSPgiLKglvZkNQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-darwin-x64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-darwin-x64/-/vite-plus-darwin-x64-1.3.0.tgz", + "integrity": "sha512-vLZTk0r+s3e7mycj4SGBbcpK+xyM8a86hXQZtQbJyW7xrGJzsavISy7/m4gGZpWN32Hl4Tmx9V/ffeaoXLkRdg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-arm64-gnu": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-arm64-gnu/-/vite-plus-linux-arm64-gnu-1.3.0.tgz", + "integrity": "sha512-H43Dj9RkEyjOFXxTk3DLTa6hqmHhxUN9vbbqNdwQoc3/TPNhC1zsLZy3qgUFbYGLdS7M9jICCqFzj5GDSYrhIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-arm64-musl": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-arm64-musl/-/vite-plus-linux-arm64-musl-1.3.0.tgz", + "integrity": "sha512-Se2GlvzUZOvFy+5hoQHoG7LFeiplKs+EJwaskayCSLwZAP+e6/LTK7EK+8OcdMpmdsLPtZoTJHkIjSzrQowJwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-x64-gnu": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-x64-gnu/-/vite-plus-linux-x64-gnu-1.3.0.tgz", + "integrity": "sha512-moxojk6zmog2SaLE4/cG75acE6X3cjR0/52Ba8OG2ct0r+dv55VSE0kellAuxS6o9VHy7TBhkIpwpFpQ34rnAA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-x64-musl": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-x64-musl/-/vite-plus-linux-x64-musl-1.3.0.tgz", + "integrity": "sha512-CykxH008Dy9u20Myki9/UIqq3Dbo1ZMqa098aS6AtWp3QKyEKzSLPB3jKDUklgmu83xiFe5BpPA+33oF0yNY7A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-win32-arm64-msvc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-win32-arm64-msvc/-/vite-plus-win32-arm64-msvc-1.3.0.tgz", + "integrity": "sha512-l61S9b8aeiDpi8wPRfz4HonqRkLpt/OVYs0sCskvlXYSW/L8qfLOtuWH/xpF8xDjMIfBefKejBVfU1UlRax5Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-win32-x64-msvc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-win32-x64-msvc/-/vite-plus-win32-x64-msvc-1.3.0.tgz", + "integrity": "sha512-b6Ng8YezA6JA3adQBNA+8Mn26+OX4fw/tsPfnk//5Cx+1XFDZoAxWYXtGzc0yUQPEKnoGtfw/0OUvcibg61yYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@yuku-core/binding-android-arm64": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-android-arm64/-/binding-android-arm64-0.19.0.tgz", + "integrity": "sha512-wdL2XmTiZixAOaHk9Z0JJZ8u9Qm8d333Zjpc6gaxXFX+UR5BlsfxZ7a2+2Rm71pamppsPYGUGFq2oVLbh6DISw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@yuku-core/binding-darwin-arm64": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-darwin-arm64/-/binding-darwin-arm64-0.19.0.tgz", + "integrity": "sha512-7sNJa6XJhyetnTy64jw3NEi4YgUcezHk1G5GH9FSvXAuC8wIlbaOwT9lgq5qo5sspjrZJQlBR1TWflP49uRpMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-core/binding-darwin-x64": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-darwin-x64/-/binding-darwin-x64-0.19.0.tgz", + "integrity": "sha512-pTC7dYEc59isdyizc3nsuyFNBu2LU5wMYZ1be6UMr59PY/AJNtO1aBLWV0MgL/z7C1nv/2sX4TNjGgKLoeevJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-core/binding-freebsd-x64": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-freebsd-x64/-/binding-freebsd-x64-0.19.0.tgz", + "integrity": "sha512-xrZjF6XOEwyLOiDIfrwDdRCS6ghxcJeirNC1KivQhVcUwlRonaU79S3/Uiyx3lBylPnT8qKJ3/aMT+H52nGTJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@yuku-core/binding-linux-arm-gnu": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.19.0.tgz", + "integrity": "sha512-0OyIH53C0I1CbADHn9FSdCfX5qbpeLwJhmOVhonFJNYheybOnkSJ50qGMfTJIb8y5zrwqvLs7FVBvz6Fk8maZg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-core/binding-linux-arm-musl": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-linux-arm-musl/-/binding-linux-arm-musl-0.19.0.tgz", + "integrity": "sha512-z/iPfkCOCSXE34VXNt0uP1ORSW+4mEa9b5CiJcWZeqZH08g49bFU/jRM2T04f+Wpw5mJ6HIzNBiojxeAbrHjeg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-core/binding-linux-arm64-gnu": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.19.0.tgz", + "integrity": "sha512-jLNTi+gzaRwBJ+gwhCJ+rDb5dmx/8hEBx93UWd7hjMSqPTL1D1wvVSvXVAldV5kALvju7b0/m51EHEhs4+I1fA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-core/binding-linux-arm64-musl": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.19.0.tgz", + "integrity": "sha512-YwyPIdW/rN2Fmv0MTFWi47FaB3F9g+2b+sJISyAw83Yd60RWH4T3Nx6XkAHHVN+uKJNBAd6So7/BVihSiFYvuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-core/binding-linux-x64-gnu": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.19.0.tgz", + "integrity": "sha512-/vskaAXVX3ESR43FiTdfiYS7z1b9uoQJwVQ/0RgUHgN2sWoAyCZs+aX4CgU7eKGIHTVfp9VFwv+c+2XchYiEtQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-core/binding-linux-x64-musl": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-linux-x64-musl/-/binding-linux-x64-musl-0.19.0.tgz", + "integrity": "sha512-FuXL5igeSKib9Rye02TdH+D6SQezTyGxIATMu1Qsn/QywrzwpGl35OLpS2C1x5vpGLeVE2XgiLd5mVPFHmIglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-core/binding-win32-arm64": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-win32-arm64/-/binding-win32-arm64-0.19.0.tgz", + "integrity": "sha512-YuJAe7zEKT1Xa7LuHlhGJpPnYQeVtvo0rZvNjJRqkH0hQGOw4C8QeP30Tg5VKGzZfo2x8CjhjEhKt4TmLwfmdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-core/binding-win32-x64": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-core/binding-win32-x64/-/binding-win32-x64-0.19.0.tgz", + "integrity": "sha512-o8iY/mXwlRkedp0LNTr4+TEfXTPQwoNZmv6k+1WIAd1ZXEn8xJJTBCtB0osaVvdZo/x/cNmV16UrWrrGpM5Mvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-toolchain/types": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@yuku-toolchain/types/-/types-0.19.0.tgz", + "integrity": "sha512-tFkNbsP4WXX9UMUC5syJGdK9dPMLK0PPaIuXCcCYH75sZ8MrebfBZ2XvCILRTw8AgLivSSCfDa4R67iYBDa4Kg==", + "dev": true, + "license": "MIT" + }, + "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": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/args-tokenizer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/args-tokenizer/-/args-tokenizer-0.3.0.tgz", + "integrity": "sha512-xXAd7G2Mll5W8uo37GETpQ2VrE84M181Z7ugHFGQnJZ50M2mbOv0osSZ9VsSgPfJQ+LVG0prSi0th+ELMsno7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/bumpp": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/bumpp/-/bumpp-12.3.0.tgz", + "integrity": "sha512-YgW09sgbIGQmAD/Jk7FEGP0tm0PeDz+Zgk+IAk+MOHhb/98pj/l3CgkxSjd7C1O99JPxTNwS+bPOTNaHuNh6lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "args-tokenizer": "^0.3.0", + "cac": "^7.0.0", + "jsonc-parser": "^3.3.1", + "package-manager-detector": "^1.8.0", + "tinyexec": "^1.3.0", + "tinyglobby": "^0.2.17", + "unconfig": "^7.5.0", + "verkit": "^0.4.0", + "yaml": "^2.9.0" + }, + "bin": { + "bumpp": "bin/bumpp.mjs" + }, + "engines": { + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" + } + }, + "node_modules/cac": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cac/-/cac-7.1.0.tgz", + "integrity": "sha512-LMDU/U2yA4IPIY8KT0NsNPWEYZvAMkbgyspxJ6btJaYV+WMDOHAZCmB0lsTObiHaRS1V/ztKAl7+TSkTqXyeoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/chai": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "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.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": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "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": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "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": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "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": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "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": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "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": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "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": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "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": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "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": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "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": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "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": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/oxfmt": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.73.0.tgz", + "integrity": "sha512-Ke4xbpuzqIBP4bkd48q815RhqWNJqOBzxVA9199M4qcF0y0LtpUv7UTg1dJ5ea6jw0RBUDZvX3qOUMNmmxfxbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinypool": "2.2.0" + }, + "bin": { + "oxfmt": "bin/oxfmt" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxfmt/binding-android-arm-eabi": "0.73.0", + "@oxfmt/binding-android-arm64": "0.73.0", + "@oxfmt/binding-darwin-arm64": "0.73.0", + "@oxfmt/binding-darwin-x64": "0.73.0", + "@oxfmt/binding-freebsd-x64": "0.73.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.73.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.73.0", + "@oxfmt/binding-linux-arm64-gnu": "0.73.0", + "@oxfmt/binding-linux-arm64-musl": "0.73.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.73.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.73.0", + "@oxfmt/binding-linux-riscv64-musl": "0.73.0", + "@oxfmt/binding-linux-s390x-gnu": "0.73.0", + "@oxfmt/binding-linux-x64-gnu": "0.73.0", + "@oxfmt/binding-linux-x64-musl": "0.73.0", + "@oxfmt/binding-openharmony-arm64": "0.73.0", + "@oxfmt/binding-win32-arm64-msvc": "0.73.0", + "@oxfmt/binding-win32-ia32-msvc": "0.73.0", + "@oxfmt/binding-win32-x64-msvc": "0.73.0" + }, + "peerDependencies": { + "@astrojs/compiler-rs": "^0.4.0 || ^0.5.0", + "svelte": "^5.0.0", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "@astrojs/compiler-rs": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/oxlint": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.88.0.tgz", + "integrity": "sha512-uIcFgw8BmqvFRGzH5+r7d2zJMEexwNSrEmM6WQNnGh3VkO0XYTEFoyxyNE6tjKMUib9Cue4XJA2DoWruO5D6aw==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.88.0", + "@oxlint/binding-android-arm64": "1.88.0", + "@oxlint/binding-darwin-arm64": "1.88.0", + "@oxlint/binding-darwin-x64": "1.88.0", + "@oxlint/binding-freebsd-x64": "1.88.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.88.0", + "@oxlint/binding-linux-arm-musleabihf": "1.88.0", + "@oxlint/binding-linux-arm64-gnu": "1.88.0", + "@oxlint/binding-linux-arm64-musl": "1.88.0", + "@oxlint/binding-linux-ppc64-gnu": "1.88.0", + "@oxlint/binding-linux-riscv64-gnu": "1.88.0", + "@oxlint/binding-linux-riscv64-musl": "1.88.0", + "@oxlint/binding-linux-s390x-gnu": "1.88.0", + "@oxlint/binding-linux-x64-gnu": "1.88.0", + "@oxlint/binding-linux-x64-musl": "1.88.0", + "@oxlint/binding-openharmony-arm64": "1.88.0", + "@oxlint/binding-win32-arm64-msvc": "1.88.0", + "@oxlint/binding-win32-ia32-msvc": "1.88.0", + "@oxlint/binding-win32-x64-msvc": "1.88.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2003", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/oxlint-tsgolint": { + "version": "7.0.2004", + "resolved": "https://registry.npmjs.org/oxlint-tsgolint/-/oxlint-tsgolint-7.0.2004.tgz", + "integrity": "sha512-vQ9WDF3fiZJvRdSDosNOflerOCOpwol6vWzVDxW76RcU19cjTh7engvkHLvmv+tzedR8fAiJUVUGr6IVZdkzXQ==", + "dev": true, + "license": "MIT", + "bin": { + "tsgolint": "bin/tsgolint.js" + }, + "optionalDependencies": { + "@oxlint-tsgolint/darwin-arm64": "7.0.2004", + "@oxlint-tsgolint/darwin-x64": "7.0.2004", + "@oxlint-tsgolint/linux-arm64": "7.0.2004", + "@oxlint-tsgolint/linux-x64": "7.0.2004", + "@oxlint-tsgolint/win32-arm64": "7.0.2004", + "@oxlint-tsgolint/win32-x64": "7.0.2004" + } + }, + "node_modules/package-manager-detector": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz", + "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/std-env": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", + "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.2.0.tgz", + "integrity": "sha512-jBrmx4lYmaC9k/mgPbylxs7kBUxHtD8256up+HjLaDFfXScKJQyil+SWXSvhAtT7XHo+yTVlpB81PGmHP8oLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/unconfig": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/unconfig/-/unconfig-7.5.0.tgz", + "integrity": "sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "defu": "^6.1.4", + "jiti": "^2.6.1", + "quansync": "^1.0.0", + "unconfig-core": "7.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unconfig-core": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/unconfig-core/-/unconfig-core-7.5.0.tgz", + "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils": { + "resolved": "packages/utils", + "link": true + }, + "node_modules/verkit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.4.1.tgz", + "integrity": "sha512-mMVzj0TXExtVdlDEq+Mzp0eyOuyznNpFobNM3uAqe3HVm/Ps2c+u17BligMkZjZCA6EiXpoon8iRTer3iu40SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vite": { + "name": "@voidzero-dev/vite-plus-core", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-core/-/vite-plus-core-1.3.0.tgz", + "integrity": "sha512-Bk/LBwNrxoB8XbGUtGJE3r+U9P4puvdZ6UH36ekygE1J+on/ytwM/yBgQwBnvmX6/MVax+2xxj8wk0N/KkyNeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/runtime": "=0.154.0", + "@oxc-project/types": "=0.154.0", + "lightningcss": "^1.33.0", + "postcss": "^8.5.29", + "yuku-parser": "0.19.0" + }, + "engines": { + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" + }, + "optionalDependencies": { + "@voidzero-dev/vite-plus-darwin-arm64": "1.3.0", + "@voidzero-dev/vite-plus-darwin-x64": "1.3.0", + "@voidzero-dev/vite-plus-linux-arm64-gnu": "1.3.0", + "@voidzero-dev/vite-plus-linux-arm64-musl": "1.3.0", + "@voidzero-dev/vite-plus-linux-x64-gnu": "1.3.0", + "@voidzero-dev/vite-plus-linux-x64-musl": "1.3.0", + "@voidzero-dev/vite-plus-win32-arm64-msvc": "1.3.0", + "@voidzero-dev/vite-plus-win32-x64-msvc": "1.3.0", + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@arethetypeswrong/core": "^0.18.1", + "@types/node": "^20.19.0 || >=22.12.0", + "@typescript/native-preview": "*", + "@vitejs/devtools": "^0.7.1", + "@volar/typescript": "~2.4.0", + "@vue/language-core": "~3.2.0 || ~3.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "publint": "^0.3.8", + "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", + "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", + "unplugin-unused": ">=0.5.0", + "unrun": "*", + "vue-tsc": "~3.2.0 || ~3.3.0", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@arethetypeswrong/core": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@typescript/native-preview": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "@volar/typescript": { + "optional": true + }, + "@vue/language-core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "publint": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "typescript": { + "optional": true + }, + "unplugin-unused": { + "optional": true + }, + "unrun": { + "optional": true + }, + "vue-tsc": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plus": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vite-plus/-/vite-plus-1.3.0.tgz", + "integrity": "sha512-wZGsDeYfPcrCegqgY68pJJHGTpEZdsZvIMH2/mWXkGkBD72UY3M7z24Sv3ZeOL/FrqbbtyqBOfkKO6BvoQ8wMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.154.0", + "@oxlint/plugins": "=1.88.0", + "@vitest/browser": "5.0.5", + "@vitest/browser-preview": "5.0.5", + "@vitest/mocker": "5.0.5", + "@vitest/pretty-format": "5.0.5", + "@vitest/snapshot": "5.0.5", + "@vitest/spy": "5.0.5", + "@vitest/utils": "5.0.5", + "oxfmt": "=0.73.0", + "oxlint": "=1.88.0", + "oxlint-tsgolint": "=7.0.2004", + "vite": "npm:@voidzero-dev/vite-plus-core@1.3.0", + "vitest": "5.0.5" + }, + "bin": { + "vp": "bin/vp", + "vpr": "bin/vpr" + }, + "engines": { + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" + }, + "optionalDependencies": { + "@voidzero-dev/vite-plus-darwin-arm64": "1.3.0", + "@voidzero-dev/vite-plus-darwin-x64": "1.3.0", + "@voidzero-dev/vite-plus-linux-arm64-gnu": "1.3.0", + "@voidzero-dev/vite-plus-linux-arm64-musl": "1.3.0", + "@voidzero-dev/vite-plus-linux-x64-gnu": "1.3.0", + "@voidzero-dev/vite-plus-linux-x64-musl": "1.3.0", + "@voidzero-dev/vite-plus-win32-arm64-msvc": "1.3.0", + "@voidzero-dev/vite-plus-win32-x64-msvc": "1.3.0" + }, + "peerDependencies": { + "@vitest/browser-playwright": "5.0.5", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "playwright": "*" + }, + "peerDependenciesMeta": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "playwright": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz", + "integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.5", + "chai": "^6.3.0", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.4.3", + "obug": "^3.0.0", + "picomatch": "^4.0.7", + "std-env": "^4.3.0", + "tinybench": "^6.2.2", + "tinyexec": "^1.3.1", + "tinyglobby": "^0.2.17", + "why-is-node-running": "3.2.1" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.5", + "@vitest/browser-preview": "5.0.5", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.5", + "@vitest/coverage-v8": "5.0.5", + "@vitest/ui": "5.0.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/website": { + "resolved": "apps/website", + "link": true + }, + "node_modules/why-is-node-running": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", + "dev": true, + "license": "MIT", + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=20.11" + } + }, + "node_modules/ws": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yuku-core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/yuku-core/-/yuku-core-0.19.0.tgz", + "integrity": "sha512-kYK9e1XdfirNhkipzW0wmMcSWcVF/CWnuugodfVYRCUwvoBefweL9HzB96OiKEhsx78V5WbUxbJqlizMX869Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.19.0" + }, + "engines": { + "node": ">=14.18.0" + }, + "optionalDependencies": { + "@yuku-core/binding-android-arm64": "0.19.0", + "@yuku-core/binding-darwin-arm64": "0.19.0", + "@yuku-core/binding-darwin-x64": "0.19.0", + "@yuku-core/binding-freebsd-x64": "0.19.0", + "@yuku-core/binding-linux-arm-gnu": "0.19.0", + "@yuku-core/binding-linux-arm-musl": "0.19.0", + "@yuku-core/binding-linux-arm64-gnu": "0.19.0", + "@yuku-core/binding-linux-arm64-musl": "0.19.0", + "@yuku-core/binding-linux-x64-gnu": "0.19.0", + "@yuku-core/binding-linux-x64-musl": "0.19.0", + "@yuku-core/binding-win32-arm64": "0.19.0", + "@yuku-core/binding-win32-x64": "0.19.0" + } + }, + "node_modules/yuku-parser": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/yuku-parser/-/yuku-parser-0.19.0.tgz", + "integrity": "sha512-XrBh9ZiQK5mJVRxZBAvD6LwRyAYrK3MFtH3+onyuxFL5hFD44EC3QOmIC1FjTj6H2MMwq46/NAI+NZxLZPZgxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.19.0", + "yuku-core": "0.19.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "packages/utils": { + "version": "0.0.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^26.6.5", + "bumpp": "^12.3.0", + "typescript": "^7.0.2", + "vite-plus": "^1.2.0" + } + } + } +} diff --git a/examples/javascript-ext/vp-demo/package.json b/examples/javascript-ext/vp-demo/package.json new file mode 100644 index 000000000..9634e5cb4 --- /dev/null +++ b/examples/javascript-ext/vp-demo/package.json @@ -0,0 +1,24 @@ +{ + "name": "vp-demo", + "version": "0.0.0", + "private": true, + "workspaces": [ + "packages/*", + "apps/*", + "tools/*" + ], + "type": "module", + "scripts": { + "ready": "vp check && vp run -r test && vp run -r build", + "dev": "vp run website#dev" + }, + "devDependencies": { + "vite-plus": "1.3.0" + }, + "overrides": { + "vite": "npm:@voidzero-dev/vite-plus-core@1.3.0" + }, + "engines": { + "node": ">=22.18.0" + } +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/packages/utils/.gitignore b/examples/javascript-ext/vp-demo/packages/utils/.gitignore new file mode 100644 index 000000000..753521168 --- /dev/null +++ b/examples/javascript-ext/vp-demo/packages/utils/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +*.log +.DS_Store diff --git a/examples/javascript-ext/vp-demo/packages/utils/README.md b/examples/javascript-ext/vp-demo/packages/utils/README.md new file mode 100644 index 000000000..fcd91a7ed --- /dev/null +++ b/examples/javascript-ext/vp-demo/packages/utils/README.md @@ -0,0 +1,23 @@ +# vite-plus-starter + +A starter for creating a Vite Plus project. + +## Development + +- Install dependencies: + +```bash +vp install +``` + +- Run the unit tests: + +```bash +vp test +``` + +- Build the library: + +```bash +vp pack +``` \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/packages/utils/package.json b/examples/javascript-ext/vp-demo/packages/utils/package.json new file mode 100644 index 000000000..088dd0cf6 --- /dev/null +++ b/examples/javascript-ext/vp-demo/packages/utils/package.json @@ -0,0 +1,39 @@ +{ + "name": "utils", + "version": "0.0.0", + "description": "A starter for creating a TypeScript package.", + "homepage": "https://github.com/author/library#readme", + "bugs": { + "url": "https://github.com/author/library/issues" + }, + "license": "MIT", + "author": "Author Name ", + "repository": { + "type": "git", + "url": "git+https://github.com/author/library.git" + }, + "files": [ + "dist" + ], + "type": "module", + "exports": { + ".": "./dist/index.mjs", + "./package.json": "./package.json" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "vp pack", + "dev": "vp pack --watch", + "test": "vp test", + "check": "vp check", + "prepublishOnly": "vp run build" + }, + "devDependencies": { + "@types/node": "^26.6.5", + "bumpp": "^12.3.0", + "typescript": "^7.0.2", + "vite-plus": "^1.2.0" + } +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/packages/utils/src/index.ts b/examples/javascript-ext/vp-demo/packages/utils/src/index.ts new file mode 100644 index 000000000..64cfeb3fa --- /dev/null +++ b/examples/javascript-ext/vp-demo/packages/utils/src/index.ts @@ -0,0 +1,3 @@ +export function fn() { + return "Hello, Vite+!"; +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/packages/utils/tests/index.test.ts b/examples/javascript-ext/vp-demo/packages/utils/tests/index.test.ts new file mode 100644 index 000000000..354dcc564 --- /dev/null +++ b/examples/javascript-ext/vp-demo/packages/utils/tests/index.test.ts @@ -0,0 +1,6 @@ +import { expect, test } from "vite-plus/test"; +import { fn } from "../src/index.ts"; + +test("fn", () => { + expect(fn()).toBe("Hello, Vite+!"); +}); \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/packages/utils/tsconfig.json b/examples/javascript-ext/vp-demo/packages/utils/tsconfig.json new file mode 100644 index 000000000..423be8fb1 --- /dev/null +++ b/examples/javascript-ext/vp-demo/packages/utils/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["es2023"], + "moduleDetection": "force", + "module": "nodenext", + "moduleResolution": "nodenext", + "resolveJsonModule": true, + "types": ["node"], + "strict": true, + "noUnusedLocals": true, + "declaration": true, + "noEmit": true, + "allowImportingTsExtensions": true, + "esModuleInterop": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "skipLibCheck": true + } +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/packages/utils/vite.config.ts b/examples/javascript-ext/vp-demo/packages/utils/vite.config.ts new file mode 100644 index 000000000..5ecec6aee --- /dev/null +++ b/examples/javascript-ext/vp-demo/packages/utils/vite.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "vite-plus"; + +export default defineConfig({ + pack: { + deps: { + // tsdown <0.23 compatibility: resolve external dependency subpaths. + // Remove to preserve subpath imports as written (the new default). + // https://tsdown.dev/options/dependencies#deps-resolvedepsubpath + resolveDepSubpath: true, + }, + dts: { + generator: "tsgo", + }, + exports: true, + }, +}); \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/tsconfig.json b/examples/javascript-ext/vp-demo/tsconfig.json new file mode 100644 index 000000000..6ccfedcc6 --- /dev/null +++ b/examples/javascript-ext/vp-demo/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "noEmit": true, + "module": "nodenext", + "moduleResolution": "nodenext", + "allowImportingTsExtensions": true, + "esModuleInterop": true + } +} \ No newline at end of file diff --git a/examples/javascript-ext/vp-demo/vite.config.ts b/examples/javascript-ext/vp-demo/vite.config.ts new file mode 100644 index 000000000..173d44f2f --- /dev/null +++ b/examples/javascript-ext/vp-demo/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vite-plus"; + +export default defineConfig({ + fmt: {}, + lint: { + jsPlugins: [{ name: "vite-plus", specifier: "vite-plus/oxlint-plugin" }], + rules: { "vite-plus/prefer-vite-plus-imports": "error" }, + options: { typeAware: true, typeCheck: true }, + }, + run: { + cache: true, + }, +}); \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/JavaScriptHostingExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/JavaScriptHostingExtensions.cs index 823b1df18..d33864dc5 100644 --- a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/JavaScriptHostingExtensions.cs +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/JavaScriptHostingExtensions.cs @@ -1,251 +1,14 @@ using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.JavaScript; -using CommunityToolkit.Aspire.Utils; using Microsoft.Extensions.Hosting; -#pragma warning disable ASPIREATS001 // AspireExport is experimental - namespace Aspire.Hosting; /// /// Provides extension methods for adding JavaScript applications to the distributed application builder. /// -public static partial class JavaScriptHostingExtensions +public static class JavaScriptHostingExtensions { - /// - /// Adds an Nx monorepo workspace to the distributed application builder. - /// - /// The to add the resource to. - /// The name of the Nx workspace resource. - /// The working directory of the Nx workspace. If not specified, it will be set to a path that is a sibling of the AppHost directory using the as the folder. - /// A reference to the . - [AspireExport] - public static IResourceBuilder AddNxApp(this IDistributedApplicationBuilder builder, [ResourceName] string name, string? workingDirectory = null) - { - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(name); - - string wd = workingDirectory ?? Path.Combine("..", name); - workingDirectory = PathNormalizer.NormalizePathForCurrentPlatform(Path.Combine(builder.AppHostDirectory, wd)); - - var resource = new NxResource(name, workingDirectory); - return builder.AddResource(resource) - .WithIconName("CodeJsRectangle") - .WithInitialState(new CustomResourceSnapshot { Properties = [], ResourceType = "NxWorkspace", State = KnownResourceStates.Running }); - } - - /// - /// Adds a Turborepo monorepo workspace to the distributed application builder. - /// - /// The to add the resource to. - /// The name of the Turborepo workspace resource. - /// The working directory of the Turborepo workspace. If not specified, it will be set to a path that is a sibling of the AppHost directory using the as the folder. - /// A reference to the . - [AspireExport] - public static IResourceBuilder AddTurborepoApp(this IDistributedApplicationBuilder builder, [ResourceName] string name, string? workingDirectory = null) - { - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(name); - - string wd = workingDirectory ?? Path.Combine("..", name); - workingDirectory = PathNormalizer.NormalizePathForCurrentPlatform(Path.Combine(builder.AppHostDirectory, wd)); - - var resource = new TurborepoResource(name, workingDirectory); - return builder.AddResource(resource) - .WithIconName("CodeJsRectangle") - .WithInitialState(new CustomResourceSnapshot { Properties = [], ResourceType = "TurborepoWorkspace", State = KnownResourceStates.Running }); - } - - /// - /// Adds an individual app to an Nx workspace. - /// - /// The Nx workspace resource builder. - /// The name of the app resource. - /// The Nx app name to run. If not specified, uses the . - /// A function to configure the app resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The configure callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder AddApp(this IResourceBuilder builder, [ResourceName] string name, string? appName = null, Func, IResourceBuilder>? configure = null) - { - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(name); - - appName ??= name; - - var resource = new NxAppResource(name, builder.Resource.WorkingDirectory, appName); - - var rb = builder.ApplicationBuilder.AddResource(resource) - .WithNodeDefaults() - .WithIconName("CodeJsRectangle") - .WithArgs("serve", appName) - .WithParentRelationship(builder.Resource); - - // If the workspace is configured to use a package manager, bring that annotation forward - if (builder.Resource.TryGetLastAnnotation(out var executionAnnotation)) - { - rb.WithCommand(executionAnnotation.ExecutableName switch - { - "npm" => "npx", - "yarn" => "yarn", - "pnpm" => "pnpx", - "bun" => "bunx", - _ => executionAnnotation.ExecutableName - }) - .WithArgs(context => - { - context.Args.Insert(0, executionAnnotation.ScriptCommand ?? "nx"); - }); - } - - // If the workspace has an installer annotation, wait for the installer to complete - if (builder.Resource.TryGetLastAnnotation(out var installerAnnotation)) - { - rb.WaitForCompletion(builder.ApplicationBuilder.CreateResourceBuilder(installerAnnotation.Resource)); - } - - configure?.Invoke(rb); - - return rb; - } - - [AspireExport(MethodName = "addApp")] - internal static IResourceBuilder AddNxWorkspaceApp(this IResourceBuilder builder, [ResourceName] string name, string? appName = null) => - builder.AddApp(name, appName, configure: null); - - /// - /// Adds an individual app to a Turborepo workspace. - /// - /// The Turborepo workspace resource builder. - /// The name of the app resource. - /// The Turborepo filter to use. If not specified, uses the . - /// A function to configure the app resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The configure callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder AddApp(this IResourceBuilder builder, [ResourceName] string name, string? filter = null, Func, IResourceBuilder>? configure = null) - { - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(name); - - filter ??= name; - - var resource = new TurborepoAppResource(name, builder.Resource.WorkingDirectory, filter); - - var rb = builder.ApplicationBuilder.AddResource(resource) - .WithNodeDefaults() - .WithIconName("CodeJsRectangle") - .WithArgs("run", "dev", "--filter", filter) - .WithParentRelationship(builder.Resource); - - // If the workspace is configured to use a package manager, bring that annotation forward - if (builder.Resource.TryGetLastAnnotation(out var executionAnnotation)) - { - rb.WithCommand(executionAnnotation.ExecutableName switch - { - "npm" => "npx", - "yarn" => "yarn", - "pnpm" => "pnpx", - "bun" => "bunx", - _ => executionAnnotation.ExecutableName - }) - .WithArgs(context => - { - context.Args.Insert(0, executionAnnotation.ScriptCommand ?? "nx"); - }); - } - - // If the workspace has an installer annotation, wait for the installer to complete - if (builder.Resource.TryGetLastAnnotation(out var installerAnnotation)) - { - rb.WaitForCompletion(builder.ApplicationBuilder.CreateResourceBuilder(installerAnnotation.Resource)); - } - - configure?.Invoke(rb); - - return rb; - } - - [AspireExport(MethodName = "addApp")] - internal static IResourceBuilder AddTurborepoWorkspaceApp(this IResourceBuilder builder, [ResourceName] string name, string? filter = null) => - builder.AddApp(name, filter, configure: null); - - /// - /// Configures the Nx workspace to use the specified JavaScript package manager when starting apps. - /// - /// The Nx workspace resource builder. - /// The package manager to use. If none is provided it will attempt to use the installer annotation's resource command. - /// A reference to the . - /// Thrown if the Nx workspace is already configured to use a different package manager. - [AspireExport("withNxPackageManagerLaunch", MethodName = "withPackageManagerLaunch")] - public static IResourceBuilder WithPackageManagerLaunch(this IResourceBuilder builder, string? packageManager = null) - { - ArgumentNullException.ThrowIfNull(builder); - - if (builder.Resource.TryGetLastAnnotation(out var existingExecution)) - { - if (packageManager is null || existingExecution.ExecutableName == packageManager) - { - // already configured with execution annotation - return builder; - } - throw new InvalidOperationException($"The Nx workspace '{builder.Resource.Name}' is already configured to run with the '{existingExecution.ExecutableName}' package manager."); - } - - // If no explicit package manager provided, try to infer from configured annotation - if (packageManager is null) - { - if (builder.Resource.TryGetLastAnnotation(out var configured)) - { - packageManager = configured.PackageManager; - } - else - { - throw new InvalidOperationException($"The Nx workspace '{builder.Resource.Name}' is not configured with a package manager. Call WithNpm/WithYarn/WithPnpm/WithBun first, or provide an explicit package manager."); - } - } - - return builder.WithAnnotation(new JavaScriptPackageManagerAnnotation(packageManager, runScriptCommand: "nx")); - } - - /// - /// Configures the Turborepo workspace to use the specified JavaScript package manager when starting apps. - /// - /// The Turborepo workspace resource builder. - /// The package manager to use. If none is provided it will attempt to use the installer annotation's resource command. - /// A reference to the . - /// Thrown if the Turborepo workspace is already configured to use a different package manager. - [AspireExport("withTurborepoPackageManagerLaunch", MethodName = "withPackageManagerLaunch")] - public static IResourceBuilder WithPackageManagerLaunch(this IResourceBuilder builder, string? packageManager = null) - { - ArgumentNullException.ThrowIfNull(builder); - - if (builder.Resource.TryGetLastAnnotation(out var existingExecution)) - { - if (packageManager is null || existingExecution.ExecutableName == packageManager) - { - // already configured with execution annotation - return builder; - } - throw new InvalidOperationException($"The Turborepo workspace '{builder.Resource.Name}' is already configured to run with the '{existingExecution.ExecutableName}' package manager."); - } - - // If no explicit package manager provided, try to infer from configured annotation - if (packageManager is null) - { - if (builder.Resource.TryGetLastAnnotation(out var configured)) - { - packageManager = configured.PackageManager; - } - else - { - throw new InvalidOperationException($"The Turborepo workspace '{builder.Resource.Name}' is not configured with a package manager. Call WithNpm/WithYarn/WithPnpm/WithBun first, or provide an explicit package manager."); - } - } - - return builder.WithAnnotation(new JavaScriptPackageManagerAnnotation(packageManager, runScriptCommand: "turbo")); - } - /// /// Maps the endpoint port for the to the appropriate command line argument. /// @@ -278,176 +41,8 @@ public static IResourceBuilder WithMappedEndpointPort(this }); } - /// - /// Configures the Nx workspace to use npm as the package manager and optionally installs packages before apps start. - /// - /// The Nx workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithNpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "npm", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withNpm")] - internal static IResourceBuilder WithNxNpm(this IResourceBuilder builder, bool install = false) => - builder.WithNpm(install, configureInstaller: null); - - /// - /// Configures the Nx workspace to use yarn as the package manager and optionally installs packages before apps start. - /// - /// The Nx workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithYarn(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "yarn", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withYarn")] - internal static IResourceBuilder WithNxYarn(this IResourceBuilder builder, bool install = false) => - builder.WithYarn(install, configureInstaller: null); - - /// - /// Configures the Nx workspace to use pnpm as the package manager and optionally installs packages before apps start. - /// - /// The Nx workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithPnpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "pnpm", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withPnpm")] - internal static IResourceBuilder WithNxPnpm(this IResourceBuilder builder, bool install = false) => - builder.WithPnpm(install, configureInstaller: null); - - /// - /// Configures the Nx workspace to use bun as the package manager and optionally installs packages before apps start. - /// - /// The Nx workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithBun(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "bun", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withBun")] - internal static IResourceBuilder WithNxBun(this IResourceBuilder builder, bool install = false) => - builder.WithBun(install, configureInstaller: null); - - /// - /// Configures the Turborepo workspace to use npm as the package manager and optionally installs packages before apps start. - /// - /// The Turborepo workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithNpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "npm", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withNpm")] - internal static IResourceBuilder WithTurborepoNpm(this IResourceBuilder builder, bool install = false) => - builder.WithNpm(install, configureInstaller: null); - - /// - /// Configures the Turborepo workspace to use yarn as the package manager and optionally installs packages before apps start. - /// - /// The Turborepo workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithYarn(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "yarn", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withYarn")] - internal static IResourceBuilder WithTurborepoYarn(this IResourceBuilder builder, bool install = false) => - builder.WithYarn(install, configureInstaller: null); - - /// - /// Configures the Turborepo workspace to use pnpm as the package manager and optionally installs packages before apps start. - /// - /// The Turborepo workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithPnpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "pnpm", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withPnpm")] - internal static IResourceBuilder WithTurborepoPnpm(this IResourceBuilder builder, bool install = false) => - builder.WithPnpm(install, configureInstaller: null); - - /// - /// Configures the Turborepo workspace to use bun as the package manager and optionally installs packages before apps start. - /// - /// The Turborepo workspace resource builder. - /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. - /// A function to configure the installer resource builder. - /// A reference to the . - /// This overload is not available in polyglot app hosts. Use instead. - [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] - public static IResourceBuilder WithBun(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) - { - ArgumentNullException.ThrowIfNull(builder); - - AddMonorepoInstaller(builder, "bun", install, configureInstaller); - return builder; - } - - [AspireExport(MethodName = "withBun")] - internal static IResourceBuilder WithTurborepoBun(this IResourceBuilder builder, bool install = false) => - builder.WithBun(install, configureInstaller: null); - - private static void AddMonorepoInstaller( - IResourceBuilder builder, + internal static void AddMonorepoInstaller( + this IResourceBuilder builder, string packageManager, bool install, Action>? configureInstaller) @@ -464,6 +59,7 @@ private static void AddMonorepoInstaller( { NxResource nx => nx.WorkingDirectory, TurborepoResource turbo => turbo.WorkingDirectory, + VitePlusResource vitePlus => vitePlus.WorkingDirectory, _ => throw new InvalidOperationException($"Unsupported resource type: {builder.Resource.GetType().Name}") }; @@ -493,7 +89,7 @@ private static void AddMonorepoInstaller( } } - private static IResourceBuilder WithNodeDefaults(this IResourceBuilder builder) where TResource : JavaScriptAppResource => + internal static IResourceBuilder WithNodeDefaults(this IResourceBuilder builder) where TResource : JavaScriptAppResource => builder.WithOtlpExporter() .WithEnvironment("NODE_ENV", builder.ApplicationBuilder.Environment.IsDevelopment() ? "development" : "production") .WithCertificateTrustConfiguration((ctx) => @@ -510,5 +106,3 @@ private static IResourceBuilder WithNodeDefaults(this IRes return Task.CompletedTask; }); } - -#pragma warning restore ASPIREATS001 // AspireExport is experimental diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/MONOREPO.md b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/MONOREPO.md index b12c2b63e..ef951c88b 100644 --- a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/MONOREPO.md +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/MONOREPO.md @@ -1,6 +1,6 @@ # Frontend Monorepo Support -This extension now provides built-in support for frontend monorepos using [Nx](https://nx.dev) and [Turborepo](https://turborepo.com). +This extension now provides built-in support for frontend monorepos using [Nx](https://nx.dev), [Turborepo](https://turborepo.com), and [Vite+](https://viteplus.dev). ## The Problem @@ -44,9 +44,21 @@ var app2 = turbo.AddApp("app2", filter: "custom-filter"); // Custom filter var app3 = turbo.AddApp("app3"); ``` +### Vite+ Support + +```csharp +var vitePlus = builder.AddVitePlusApp("vp-demo", workingDirectory: "../frontend") + .WithNpm(install: true); // Single shared installer + +var app1 = vitePlus.AddApp("website"); // Defaults to the apps/website directory +var app2 = vitePlus.AddApp("docs", directory: "packages/docs"); // Custom directory +``` + +Vite+ targets packages with the global `-C` flag (`vp -C apps/web dev`), which runs the command as if you had `cd`'d into the package first. This is different from passing a directory as a positional argument (`vp dev apps/web`), which only sets Vite's `root` option without changing the working directory. + ## Package Managers -Both Nx and Turborepo support yarn, pnpm, and bun package managers: +Nx, Turborepo, and Vite+ support yarn, pnpm, and bun package managers: - `.WithYarn(install: true)` - uses yarn - `.WithPnpm(install: true)` - uses pnpm @@ -76,9 +88,9 @@ nxDefault.AddApp("app1"); // Runs: nx serve app1 (no package manager prefix) **Command Generation Examples:** -- `WithPackageManagerLaunch("yarn")` → `yarn nx serve app1` or `yarn turbo run dev --filter app1` -- `WithPackageManagerLaunch("pnpm")` → `pnpm nx serve app1` or `pnpm turbo run dev --filter app1` -- No `WithPackageManagerLaunch()` → `nx serve app1` or `turbo run dev --filter app1` +- `WithPackageManagerLaunch("yarn")` → `yarn nx serve app1`, `yarn turbo run dev --filter app1`, or `yarn vp -C apps/web dev` +- `WithPackageManagerLaunch("pnpm")` → `pnpm nx serve app1`, `pnpm turbo run dev --filter app1`, or `pnpm vp -C apps/web dev` +- No `WithPackageManagerLaunch()` → `nx serve app1`, `turbo run dev --filter app1`, or `vp -C apps/web dev` ## How It Works @@ -98,6 +110,11 @@ nxDefault.AddApp("app1"); // Runs: nx serve app1 (no package manager prefix) - Command: `turbo run dev --filter {filter}` - The `filter` parameter controls which packages Turborepo builds/serves +### Vite+ Apps + +- Command: `vp -C {directory} dev` +- The `directory` parameter is passed to the global `-C` flag so the built-in Vite dev server runs with the package as its working directory. It defaults to `apps/{name}` when not specified + ## Migration If you're currently using the workaround pattern: diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxAppResource.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxAppResource.cs index eff0932d5..0126adcaa 100644 --- a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxAppResource.cs +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxAppResource.cs @@ -9,7 +9,7 @@ namespace Aspire.Hosting.ApplicationModel; /// The working directory of the application. /// The Nx app name to run (used in 'nx serve {appName}'). /// The command to run (default is 'nx'). -public class NxAppResource(string name, string workingDirectory, string appName, string command = "nx") +public class NxAppResource(string name, string workingDirectory, string appName, string command = NxHostingExtensions.CliName) : JavaScriptAppResource(name, command, workingDirectory) { /// diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxHostingExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxHostingExtensions.cs new file mode 100644 index 000000000..45254f00f --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxHostingExtensions.cs @@ -0,0 +1,214 @@ +using Aspire.Hosting.ApplicationModel; +using Aspire.Hosting.JavaScript; +using CommunityToolkit.Aspire.Utils; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Nx applications to the distributed application builder. +/// +public static class NxHostingExtensions +{ + internal const string CliName = "nx"; + + /// + /// Adds an Nx monorepo workspace to the distributed application builder. + /// + /// The to add the resource to. + /// The name of the Nx workspace resource. + /// The working directory of the Nx workspace. If not specified, it will be set to a path that is a sibling of the AppHost directory using the as the folder. + /// A reference to the . + [AspireExport] + public static IResourceBuilder AddNxApp(this IDistributedApplicationBuilder builder, [ResourceName] string name, string? workingDirectory = null) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + string wd = workingDirectory ?? Path.Combine("..", name); + workingDirectory = Path.Combine(builder.AppHostDirectory, wd).NormalizePathForCurrentPlatform(); + + var resource = new NxResource(name, workingDirectory); + return builder.AddResource(resource) + .WithIconName("CodeJsRectangle") + .WithInitialState(new CustomResourceSnapshot { Properties = [], ResourceType = "NxWorkspace", State = KnownResourceStates.Running }); + } + + /// + /// Adds an individual app to an Nx workspace. + /// + /// The Nx workspace resource builder. + /// The name of the app resource. + /// The Nx app name to run. If not specified, uses the . + /// A function to configure the app resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The configure callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder AddApp(this IResourceBuilder builder, [ResourceName] string name, string? appName = null, Func, IResourceBuilder>? configure = null) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + appName ??= name; + + var resource = new NxAppResource(name, builder.Resource.WorkingDirectory, appName); + + var rb = builder.ApplicationBuilder.AddResource(resource) + .WithNodeDefaults() + .WithIconName("CodeJsRectangle") + .WithArgs("serve", appName) + .WithParentRelationship(builder.Resource); + + // If the workspace is configured to use a package manager, bring that annotation forward + if (builder.Resource.TryGetLastAnnotation(out var executionAnnotation)) + { + rb.WithCommand(executionAnnotation.ExecutableName switch + { + "npm" => "npx", + "yarn" => "yarn", + "pnpm" => "pnpx", + "bun" => "bunx", + _ => executionAnnotation.ExecutableName + }) + .WithArgs(context => + { + context.Args.Insert(0, executionAnnotation.ScriptCommand ?? CliName); + }); + } + + // If the workspace has an installer annotation, wait for the installer to complete + if (builder.Resource.TryGetLastAnnotation(out var installerAnnotation)) + { + rb.WaitForCompletion(builder.ApplicationBuilder.CreateResourceBuilder(installerAnnotation.Resource)); + } + + configure?.Invoke(rb); + + return rb; + } + + [AspireExport(MethodName = "addApp")] + internal static IResourceBuilder AddNxWorkspaceApp(this IResourceBuilder builder, [ResourceName] string name, string? appName = null) => + builder.AddApp(name, appName, configure: null); + + /// + /// Configures the Nx workspace to use the specified JavaScript package manager when starting apps. + /// + /// The Nx workspace resource builder. + /// The package manager to use. If none is provided it will attempt to use the installer annotation's resource command. + /// A reference to the . + /// Thrown if the Nx workspace is already configured to use a different package manager. + [AspireExport("withNxPackageManagerLaunch", MethodName = "withPackageManagerLaunch")] + public static IResourceBuilder WithPackageManagerLaunch(this IResourceBuilder builder, string? packageManager = null) + { + ArgumentNullException.ThrowIfNull(builder); + + if (builder.Resource.TryGetLastAnnotation(out var existingExecution)) + { + if (packageManager is null || existingExecution.ExecutableName == packageManager) + { + // already configured with execution annotation + return builder; + } + throw new InvalidOperationException($"The Nx workspace '{builder.Resource.Name}' is already configured to run with the '{existingExecution.ExecutableName}' package manager."); + } + + // If no explicit package manager provided, try to infer from configured annotation + if (packageManager is null) + { + if (builder.Resource.TryGetLastAnnotation(out var configured)) + { + packageManager = configured.PackageManager; + } + else + { + throw new InvalidOperationException($"The Nx workspace '{builder.Resource.Name}' is not configured with a package manager. Call WithNpm/WithYarn/WithPnpm/WithBun first, or provide an explicit package manager."); + } + } + + return builder.WithAnnotation(new JavaScriptPackageManagerAnnotation(packageManager, runScriptCommand: CliName)); + } + + /// + /// Configures the Nx workspace to use npm as the package manager and optionally installs packages before apps start. + /// + /// The Nx workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithNpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("npm", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withNpm")] + internal static IResourceBuilder WithNxNpm(this IResourceBuilder builder, bool install = false) => + builder.WithNpm(install, configureInstaller: null); + + /// + /// Configures the Nx workspace to use yarn as the package manager and optionally installs packages before apps start. + /// + /// The Nx workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithYarn(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("yarn", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withYarn")] + internal static IResourceBuilder WithNxYarn(this IResourceBuilder builder, bool install = false) => + builder.WithYarn(install, configureInstaller: null); + + /// + /// Configures the Nx workspace to use pnpm as the package manager and optionally installs packages before apps start. + /// + /// The Nx workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithPnpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("pnpm", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withPnpm")] + internal static IResourceBuilder WithNxPnpm(this IResourceBuilder builder, bool install = false) => + builder.WithPnpm(install, configureInstaller: null); + + /// + /// Configures the Nx workspace to use bun as the package manager and optionally installs packages before apps start. + /// + /// The Nx workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithBun(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("bun", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withBun")] + internal static IResourceBuilder WithNxBun(this IResourceBuilder builder, bool install = false) => + builder.WithBun(install, configureInstaller: null); +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxResource.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxResource.cs index 321bbef3c..9a4620e12 100644 --- a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxResource.cs +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/NxResource.cs @@ -1,5 +1,3 @@ -using Aspire.Hosting.ApplicationModel; - namespace Aspire.Hosting.ApplicationModel; /// diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/README.md b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/README.md index 0dbd2582a..009681e6d 100644 --- a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/README.md +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/README.md @@ -1,6 +1,6 @@ # CommunityToolkit.Aspire.Hosting.JavaScript.Extensions library -This integration contains extensions for the [Node.js hosting package](https://nuget.org/packages/Aspire.Hosting.JavaScript) for Aspire, including support for frontend monorepos (Nx, Turborepo). +This integration contains extensions for the [Node.js hosting package](https://nuget.org/packages/Aspire.Hosting.JavaScript) for Aspire, including support for frontend monorepos (Nx, Turborepo, Vite+). ## Getting Started @@ -31,6 +31,14 @@ var turbo = builder.AddTurborepoApp("turbo", workingDirectory: "../frontend") var turboApp1 = turbo.AddApp("app1"); var turboApp2 = turbo.AddApp("app2", filter: "custom-filter"); + +// Vite+ workspace +var vitePlus = builder.AddVitePlusApp("vp-demo") + .WithNpm(install: true) + .WithPackageManagerLaunch(); + +vitePlus.AddApp("website"); // Runs: npx vp -C apps/website dev +vitePlus.AddApp("docs", directory: "packages/docs"); ``` See [MONOREPO.md](./MONOREPO.md) for detailed documentation on monorepo support. @@ -59,8 +67,11 @@ var bunRepo = builder.AddTurborepoApp("bun-repo", workingDirectory: "../frontend // Nx with yarn: yarn nx serve app1 // Turborepo with pnpm: pnpm turbo run dev --filter app1 // Turborepo with bun: bunx turbo run dev --filter app1 +// Vite+ with npm: npx vp -C apps/website dev ``` +> **Note**: Vite's dev server moves to the next free port if the assigned one is taken. When mapping an endpoint, consider adding `--strictPort` via `WithArgs` so the app fails fast instead of drifting away from the mapped port. + ### Package installation with custom flags You can pass additional flags to package managers during installation: diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoAppResource.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoAppResource.cs index ae9194a46..8073a85b7 100644 --- a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoAppResource.cs +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoAppResource.cs @@ -9,7 +9,7 @@ namespace Aspire.Hosting.ApplicationModel; /// The working directory of the application. /// The Turborepo filter to use (used in 'turbo run dev --filter={filter}'). /// The command to run (default is 'turbo'). -public class TurborepoAppResource(string name, string workingDirectory, string filter, string command = "turbo") +public class TurborepoAppResource(string name, string workingDirectory, string filter, string command = TurborepoHostingExtensions.CliName) : JavaScriptAppResource(name, command, workingDirectory) { /// diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoHostingExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoHostingExtensions.cs new file mode 100644 index 000000000..6cda76955 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoHostingExtensions.cs @@ -0,0 +1,214 @@ +using Aspire.Hosting.ApplicationModel; +using Aspire.Hosting.JavaScript; +using CommunityToolkit.Aspire.Utils; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Turborepo applications to the distributed application builder. +/// +public static class TurborepoHostingExtensions +{ + internal const string CliName = "turbo"; + + /// + /// Adds a Turborepo monorepo workspace to the distributed application builder. + /// + /// The to add the resource to. + /// The name of the Turborepo workspace resource. + /// The working directory of the Turborepo workspace. If not specified, it will be set to a path that is a sibling of the AppHost directory using the as the folder. + /// A reference to the . + [AspireExport] + public static IResourceBuilder AddTurborepoApp(this IDistributedApplicationBuilder builder, [ResourceName] string name, string? workingDirectory = null) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + string wd = workingDirectory ?? Path.Combine("..", name); + workingDirectory = Path.Combine(builder.AppHostDirectory, wd).NormalizePathForCurrentPlatform(); + + var resource = new TurborepoResource(name, workingDirectory); + return builder.AddResource(resource) + .WithIconName("CodeJsRectangle") + .WithInitialState(new CustomResourceSnapshot { Properties = [], ResourceType = "TurborepoWorkspace", State = KnownResourceStates.Running }); + } + + /// + /// Adds an individual app to a Turborepo workspace. + /// + /// The Turborepo workspace resource builder. + /// The name of the app resource. + /// The Turborepo filter to use. If not specified, uses the . + /// A function to configure the app resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The configure callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder AddApp(this IResourceBuilder builder, [ResourceName] string name, string? filter = null, Func, IResourceBuilder>? configure = null) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + filter ??= name; + + var resource = new TurborepoAppResource(name, builder.Resource.WorkingDirectory, filter); + + var rb = builder.ApplicationBuilder.AddResource(resource) + .WithNodeDefaults() + .WithIconName("CodeJsRectangle") + .WithArgs("run", "dev", "--filter", filter) + .WithParentRelationship(builder.Resource); + + // If the workspace is configured to use a package manager, bring that annotation forward + if (builder.Resource.TryGetLastAnnotation(out var executionAnnotation)) + { + rb.WithCommand(executionAnnotation.ExecutableName switch + { + "npm" => "npx", + "yarn" => "yarn", + "pnpm" => "pnpx", + "bun" => "bunx", + _ => executionAnnotation.ExecutableName + }) + .WithArgs(context => + { + context.Args.Insert(0, executionAnnotation.ScriptCommand ?? CliName); + }); + } + + // If the workspace has an installer annotation, wait for the installer to complete + if (builder.Resource.TryGetLastAnnotation(out var installerAnnotation)) + { + rb.WaitForCompletion(builder.ApplicationBuilder.CreateResourceBuilder(installerAnnotation.Resource)); + } + + configure?.Invoke(rb); + + return rb; + } + + [AspireExport(MethodName = "addApp")] + internal static IResourceBuilder AddTurborepoWorkspaceApp(this IResourceBuilder builder, [ResourceName] string name, string? filter = null) => + builder.AddApp(name, filter, configure: null); + + /// + /// Configures the Turborepo workspace to use the specified JavaScript package manager when starting apps. + /// + /// The Turborepo workspace resource builder. + /// The package manager to use. If none is provided it will attempt to use the installer annotation's resource command. + /// A reference to the . + /// Thrown if the Turborepo workspace is already configured to use a different package manager. + [AspireExport("withTurborepoPackageManagerLaunch", MethodName = "withPackageManagerLaunch")] + public static IResourceBuilder WithPackageManagerLaunch(this IResourceBuilder builder, string? packageManager = null) + { + ArgumentNullException.ThrowIfNull(builder); + + if (builder.Resource.TryGetLastAnnotation(out var existingExecution)) + { + if (packageManager is null || existingExecution.ExecutableName == packageManager) + { + // already configured with execution annotation + return builder; + } + throw new InvalidOperationException($"The Turborepo workspace '{builder.Resource.Name}' is already configured to run with the '{existingExecution.ExecutableName}' package manager."); + } + + // If no explicit package manager provided, try to infer from configured annotation + if (packageManager is null) + { + if (builder.Resource.TryGetLastAnnotation(out var configured)) + { + packageManager = configured.PackageManager; + } + else + { + throw new InvalidOperationException($"The Turborepo workspace '{builder.Resource.Name}' is not configured with a package manager. Call WithNpm/WithYarn/WithPnpm/WithBun first, or provide an explicit package manager."); + } + } + + return builder.WithAnnotation(new JavaScriptPackageManagerAnnotation(packageManager, runScriptCommand: CliName)); + } + + /// + /// Configures the Turborepo workspace to use npm as the package manager and optionally installs packages before apps start. + /// + /// The Turborepo workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithNpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("npm", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withNpm")] + internal static IResourceBuilder WithTurborepoNpm(this IResourceBuilder builder, bool install = false) => + builder.WithNpm(install, configureInstaller: null); + + /// + /// Configures the Turborepo workspace to use yarn as the package manager and optionally installs packages before apps start. + /// + /// The Turborepo workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithYarn(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("yarn", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withYarn")] + internal static IResourceBuilder WithTurborepoYarn(this IResourceBuilder builder, bool install = false) => + builder.WithYarn(install, configureInstaller: null); + + /// + /// Configures the Turborepo workspace to use pnpm as the package manager and optionally installs packages before apps start. + /// + /// The Turborepo workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithPnpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("pnpm", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withPnpm")] + internal static IResourceBuilder WithTurborepoPnpm(this IResourceBuilder builder, bool install = false) => + builder.WithPnpm(install, configureInstaller: null); + + /// + /// Configures the Turborepo workspace to use bun as the package manager and optionally installs packages before apps start. + /// + /// The Turborepo workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithBun(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("bun", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withBun")] + internal static IResourceBuilder WithTurborepoBun(this IResourceBuilder builder, bool install = false) => + builder.WithBun(install, configureInstaller: null); +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoResource.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoResource.cs index b6baabd06..e00863dd4 100644 --- a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoResource.cs +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/TurborepoResource.cs @@ -1,5 +1,3 @@ -using Aspire.Hosting.ApplicationModel; - namespace Aspire.Hosting.ApplicationModel; /// diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusAppResource.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusAppResource.cs new file mode 100644 index 000000000..2d0066f6c --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusAppResource.cs @@ -0,0 +1,19 @@ +using Aspire.Hosting.JavaScript; + +namespace Aspire.Hosting.ApplicationModel; + +/// +/// A resource that represents a Node.js application running under Vite+. +/// +/// The name of the resource. +/// The working directory of the Vite+ workspace. +/// The directory of the app within the workspace, relative to the workspace root. It is passed to vp -C. +/// The command to run (default is 'vp'). +public class VitePlusAppResource(string name, string workingDirectory, string directory, string command = VitePlusHostingExtensions.CliName) + : JavaScriptAppResource(name, command, workingDirectory) +{ + /// + /// Gets the directory of the app within the Vite+ workspace, relative to the workspace root. + /// + public string Directory { get; } = directory; +} diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusHostingExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusHostingExtensions.cs new file mode 100644 index 000000000..f0f9ba734 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusHostingExtensions.cs @@ -0,0 +1,214 @@ +using Aspire.Hosting.ApplicationModel; +using Aspire.Hosting.JavaScript; +using CommunityToolkit.Aspire.Utils; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Vite+ applications to the distributed application builder. +/// +public static class VitePlusHostingExtensions +{ + internal const string CliName = "vp"; + + /// + /// Adds a Vite+ monorepo workspace to the distributed application builder. + /// + /// The to add the resource to. + /// The name of the Vite+ workspace resource. + /// The working directory of the Vite+ workspace. If not specified, it will be set to a path that is a sibling of the AppHost directory using the as the folder. + /// A reference to the . + [AspireExport] + public static IResourceBuilder AddVitePlusApp(this IDistributedApplicationBuilder builder, [ResourceName] string name, string? workingDirectory = null) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + string wd = workingDirectory ?? Path.Combine("..", name); + workingDirectory = Path.Combine(builder.AppHostDirectory, wd).NormalizePathForCurrentPlatform(); + + var resource = new VitePlusResource(name, workingDirectory); + return builder.AddResource(resource) + .WithIconName("CodeJsRectangle") + .WithInitialState(new CustomResourceSnapshot { Properties = [], ResourceType = "VitePlusWorkspace", State = KnownResourceStates.Running }); + } + + /// + /// Adds an individual app to a Vite+ workspace. + /// + /// The Vite+ workspace resource builder. + /// The name of the app resource. + /// The directory of the app within the Vite+ workspace, relative to the workspace root. It is passed to vp -C so the app behaves as if you had cd'd into it. If not specified, defaults to apps/{name}. + /// A function to configure the app resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The configure callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder AddApp(this IResourceBuilder builder, [ResourceName] string name, string? directory = null, Func, IResourceBuilder>? configure = null) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + directory ??= Path.Combine("apps", name); + + var resource = new VitePlusAppResource(name, builder.Resource.WorkingDirectory, directory); + + var rb = builder.ApplicationBuilder.AddResource(resource) + .WithNodeDefaults() + .WithIconName("CodeJsRectangle") + .WithArgs("-C", directory, "dev") + .WithParentRelationship(builder.Resource); + + // If the workspace is configured to use a package manager, bring that annotation forward + if (builder.Resource.TryGetLastAnnotation(out var executionAnnotation)) + { + rb.WithCommand(executionAnnotation.ExecutableName switch + { + "npm" => "npx", + "yarn" => "yarn", + "pnpm" => "pnpx", + "bun" => "bunx", + _ => executionAnnotation.ExecutableName + }) + .WithArgs(context => + { + context.Args.Insert(0, executionAnnotation.ScriptCommand ?? CliName); + }); + } + + // If the workspace has an installer annotation, wait for the installer to complete + if (builder.Resource.TryGetLastAnnotation(out var installerAnnotation)) + { + rb.WaitForCompletion(builder.ApplicationBuilder.CreateResourceBuilder(installerAnnotation.Resource)); + } + + configure?.Invoke(rb); + + return rb; + } + + [AspireExport(MethodName = "addApp")] + internal static IResourceBuilder AddVitePlusWorkspaceApp(this IResourceBuilder builder, [ResourceName] string name, string? directory = null) => + builder.AddApp(name, directory, configure: null); + + /// + /// Configures the Vite+ workspace to use the specified JavaScript package manager when starting apps. + /// + /// The Vite+ workspace resource builder. + /// The package manager to use. If none is provided it will attempt to use the installer annotation's resource command. + /// A reference to the . + /// Thrown if the Vite+ workspace is already configured to use a different package manager. + [AspireExport("withVitePlusPackageManagerLaunch", MethodName = "withPackageManagerLaunch")] + public static IResourceBuilder WithPackageManagerLaunch(this IResourceBuilder builder, string? packageManager = null) + { + ArgumentNullException.ThrowIfNull(builder); + + if (builder.Resource.TryGetLastAnnotation(out var existingExecution)) + { + if (packageManager is null || existingExecution.ExecutableName == packageManager) + { + // already configured with execution annotation + return builder; + } + throw new InvalidOperationException($"The Vite+ workspace '{builder.Resource.Name}' is already configured to run with the '{existingExecution.ExecutableName}' package manager."); + } + + // If no explicit package manager provided, try to infer from configured annotation + if (packageManager is null) + { + if (builder.Resource.TryGetLastAnnotation(out var configured)) + { + packageManager = configured.PackageManager; + } + else + { + throw new InvalidOperationException($"The Vite+ workspace '{builder.Resource.Name}' is not configured with a package manager. Call WithNpm/WithYarn/WithPnpm/WithBun first, or provide an explicit package manager."); + } + } + + return builder.WithAnnotation(new JavaScriptPackageManagerAnnotation(packageManager, runScriptCommand: CliName)); + } + + /// + /// Configures the Vite+ workspace to use npm as the package manager and optionally installs packages before apps start. + /// + /// The Vite+ workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithNpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("npm", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withNpm")] + internal static IResourceBuilder WithVitePlusNpm(this IResourceBuilder builder, bool install = false) => + builder.WithNpm(install, configureInstaller: null); + + /// + /// Configures the Vite+ workspace to use yarn as the package manager and optionally installs packages before apps start. + /// + /// The Vite+ workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithYarn(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("yarn", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withYarn")] + internal static IResourceBuilder WithVitePlusYarn(this IResourceBuilder builder, bool install = false) => + builder.WithYarn(install, configureInstaller: null); + + /// + /// Configures the Vite+ workspace to use pnpm as the package manager and optionally installs packages before apps start. + /// + /// The Vite+ workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithPnpm(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("pnpm", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withPnpm")] + internal static IResourceBuilder WithVitePlusPnpm(this IResourceBuilder builder, bool install = false) => + builder.WithPnpm(install, configureInstaller: null); + + /// + /// Configures the Vite+ workspace to use bun as the package manager and optionally installs packages before apps start. + /// + /// The Vite+ workspace resource builder. + /// When true, automatically installs packages before apps start. When false (default), only sets the package manager annotation without creating an installer resource. + /// A function to configure the installer resource builder. + /// A reference to the . + /// This overload is not available in polyglot app hosts. Use instead. + [AspireExportIgnore(Reason = "The installer configuration callback is not ATS-compatible. Use the overload without the configure callback instead.")] + public static IResourceBuilder WithBun(this IResourceBuilder builder, bool install = false, Action>? configureInstaller = null) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.AddMonorepoInstaller("bun", install, configureInstaller); + return builder; + } + + [AspireExport(MethodName = "withBun")] + internal static IResourceBuilder WithVitePlusBun(this IResourceBuilder builder, bool install = false) => + builder.WithBun(install, configureInstaller: null); +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusResource.cs b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusResource.cs new file mode 100644 index 000000000..3ef61df6b --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/VitePlusResource.cs @@ -0,0 +1,14 @@ +namespace Aspire.Hosting.ApplicationModel; + +/// +/// A resource that represents an Vite+ monorepo workspace. +/// +/// The name of the resource. +/// The working directory of the Vite+ workspace. +public class VitePlusResource(string name, string workingDirectory) : Resource(name) +{ + /// + /// Gets the working directory of the Vite+ workspace. + /// + public string WorkingDirectory { get; } = workingDirectory; +} \ No newline at end of file diff --git a/tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/AppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/AppHostTests.cs index fd01a487a..8cfc6a036 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/AppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/AppHostTests.cs @@ -8,6 +8,7 @@ public class AppHostTests(AspireIntegrationTestFixture(); + var vitePlusResource = Assert.Single(appModel.Resources.OfType()); + + Assert.Equal("test-vp", vitePlusResource.Name); + Assert.NotEmpty(vitePlusResource.WorkingDirectory); + } + + [Fact] + public void VitePlusResource_DefaultWorkingDirectory() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("my-vp"); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + var vitePlusResource = Assert.Single(appModel.Resources.OfType()); + + Assert.Contains("my-vp", vitePlusResource.WorkingDirectory); + } + + [Fact] + public void VitePlusResource_IsRunningState() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-running"); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + var vpResource = Assert.Single(appModel.Resources.OfType()); + + Assert.True(vpResource.TryGetAnnotationsOfType(out var annotations)); + var annotation = Assert.Single(annotations); + + Assert.Equal(KnownResourceStates.Running, annotation.InitialSnapshot.State?.Text); + } + + [Fact] + public async Task VitePlus_AddApp_DefaultDirectoryIsAppsName() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-default-dir"); + vp.AddApp("app1"); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + var vpApp = Assert.Single(appModel.Resources.OfType()); + + Assert.Equal(Path.Combine("apps", "app1"), vpApp.Directory); + Assert.Equal("vp", vpApp.Command); + var args = await vpApp.GetArgumentListAsync(); + Assert.Collection(args, + arg => Assert.Equal("-C", arg), + arg => Assert.Equal(Path.Combine("apps", "app1"), arg), + arg => Assert.Equal("dev", arg)); + } + + [Fact] + public async Task VitePlus_AddApp_ExplicitDirectoryIsPassedThrough() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-explicit-dir"); + vp.AddApp("docs", directory: "packages/docs"); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + var vpApp = Assert.Single(appModel.Resources.OfType()); + + Assert.Equal("packages/docs", vpApp.Directory); + Assert.Equal("vp", vpApp.Command); + var args = await vpApp.GetArgumentListAsync(); + Assert.Collection(args, + arg => Assert.Equal("-C", arg), + arg => Assert.Equal("packages/docs", arg), + arg => Assert.Equal("dev", arg)); + } + + [Theory] + [InlineData("npm")] + [InlineData("yarn")] + [InlineData("pnpm")] + [InlineData("bun")] + public async Task VitePlus_WithPackageManagerLaunch_InfersFromInstallerWhenNotProvided(string packageManager) + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-default"); + + vp = (packageManager switch + { + "npm" => vp.WithNpm(), + "yarn" => vp.WithYarn(), + "pnpm" => vp.WithPnpm(), + "bun" => vp.WithBun(), + _ => throw new ArgumentOutOfRangeException(nameof(packageManager), $"Unsupported package manager: {packageManager}"), + }).WithPackageManagerLaunch(); + + // Add an app to the Vite+ workspace to verify app-level command/args + var app1 = vp.AddApp("app1"); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + var vpResource = Assert.Single(appModel.Resources.OfType()); + + Assert.True(vpResource.TryGetLastAnnotation(out var pm)); + Assert.Equal(packageManager, pm.ExecutableName); + + // Verify Vite+ app command and args + var vpApp = Assert.Single(appModel.Resources.OfType()); + // For the inferred package manager, AddApp uses the corresponding command + Assert.Equal(packageManager switch + { + "npm" => "npx", + "yarn" => "yarn", + "pnpm" => "pnpx", + "bun" => "bunx", + _ => packageManager + }, vpApp.Command); + var vpArgs = await vpApp.GetArgumentListAsync(); + Assert.Collection(vpArgs, + arg => Assert.Equal("vp", arg), + arg => Assert.Equal("-C", arg), + arg => Assert.Equal(Path.Combine("apps", "app1"), arg), + arg => Assert.Equal("dev", arg)); + } + + [Fact] + public async Task VitePlus_WithPackageManagerLaunch_WithPnpmAndYarn() + { + var builder = DistributedApplication.CreateBuilder(); + + var vpPnpm = builder.AddVitePlusApp("vp-pnpm").WithPnpm().WithPackageManagerLaunch(); + var vpYarn = builder.AddVitePlusApp("vp-yarn").WithYarn().WithPackageManagerLaunch(); + + vpPnpm.AddApp("app1-pnpm"); + vpYarn.AddApp("app1-yarn"); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + + var vpPnpmResource = appModel.Resources.OfType().Single(r => r.Name == "vp-pnpm"); + Assert.True(vpPnpmResource.TryGetLastAnnotation(out var vpmPnpm)); + Assert.Equal("pnpm", vpmPnpm.ExecutableName); + + var vpPnpmApp = appModel.Resources.OfType().Single(r => r.Name == "app1-pnpm"); + Assert.Equal("pnpx", vpPnpmApp.Command); + var vppnpmArgs = await vpPnpmApp.GetArgumentListAsync(); + Assert.Collection(vppnpmArgs, + arg => Assert.Equal("vp", arg), + arg => Assert.Equal("-C", arg), + arg => Assert.Equal(Path.Combine("apps", "app1-pnpm"), arg), + arg => Assert.Equal("dev", arg)); + + var vpYarnResource = appModel.Resources.OfType().Single(r => r.Name == "vp-yarn"); + Assert.True(vpYarnResource.TryGetLastAnnotation(out var vpmYarn)); + Assert.Equal("yarn", vpmYarn.ExecutableName); + + var vpYarnApp = appModel.Resources.OfType().Single(r => r.Name == "app1-yarn"); + Assert.Equal("yarn", vpYarnApp.Command); + var vpyarnArgs = await vpYarnApp.GetArgumentListAsync(); + Assert.Collection(vpyarnArgs, + arg => Assert.Equal("vp", arg), + arg => Assert.Equal("-C", arg), + arg => Assert.Equal(Path.Combine("apps", "app1-yarn"), arg), + arg => Assert.Equal("dev", arg)); + } + + [Fact] + public async Task VitePlus_NoWithPackageManagerLaunch_Defaults_AppCommandsIncludeNpxOrDefault() + { + var builder = DistributedApplication.CreateBuilder(); + + // Vite+ without WithPackageManagerLaunch or installer + var vp = builder.AddVitePlusApp("vp-default"); + vp.AddApp("app-vp-default"); + + using var app = builder.Build(); + + var appModel = app.Services.GetRequiredService(); + + var vpApp = appModel.Resources.OfType().Single(r => r.Name == "app-vp-default"); + // Command should be 'vp' (default) and args should target the app directory via -C + Assert.Equal("vp", vpApp.Command); + var vpArgs = await vpApp.GetArgumentListAsync(); + Assert.Collection(vpArgs, + arg => Assert.Equal("-C", arg), + arg => Assert.Equal(Path.Combine("apps", "app-vp-default"), arg), + arg => Assert.Equal("dev", arg)); + } + + [Theory] + [InlineData("npm")] + [InlineData("yarn")] + [InlineData("pnpm")] + [InlineData("bun")] + public async Task VitePlus_WithPackageManager_WithoutRunWith_DoesNotAffectAppExecution(string packageManager) + { + var builder = DistributedApplication.CreateBuilder(); + + var vpBuilder = builder.AddVitePlusApp("vp-configured-only"); + vpBuilder = packageManager switch + { + "npm" => vpBuilder.WithNpm(), + "yarn" => vpBuilder.WithYarn(), + "pnpm" => vpBuilder.WithPnpm(), + "bun" => vpBuilder.WithBun(), + _ => throw new ArgumentOutOfRangeException(nameof(packageManager)) + }; + + vpBuilder.AddApp("test-app"); + + using var app = builder.Build(); + var appModel = app.Services.GetRequiredService(); + + // Workspace should have configured annotation but not execution annotation + var vpResource = Assert.Single(appModel.Resources.OfType()); + Assert.True(vpResource.TryGetLastAnnotation(out var configured)); + Assert.Equal(packageManager, configured.PackageManager); + Assert.False(vpResource.TryGetLastAnnotation(out _)); + + // App should use "vp" directly, not wrapped via package manager + var vpApp = Assert.Single(appModel.Resources.OfType()); + Assert.Equal("vp", vpApp.Command); + var args = await vpApp.GetArgumentListAsync(); + Assert.Collection(args, + arg => Assert.Equal("-C", arg), + arg => Assert.Equal(Path.Combine("apps", "test-app"), arg), + arg => Assert.Equal("dev", arg)); + } + + [Fact] + public void VitePlus_WithPackageManagerLaunch_CalledTwiceWithDifferent_Throws() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-conflict") + .WithPnpm() + .WithPackageManagerLaunch(); + + var ex = Assert.Throws(() => vp.WithPackageManagerLaunch("npm")); + Assert.Contains("already configured to run with the 'pnpm' package manager", ex.Message); + } + + [Fact] + public void VitePlus_WithPackageManagerLaunch_ThrowsWhenNotConfigured() + { + var builder = DistributedApplication.CreateBuilder(); + + var ex = Assert.Throws(() => + builder.AddVitePlusApp("vp-no-config").WithPackageManagerLaunch()); + + Assert.Contains("not configured with a package manager", ex.Message); + } + + [Fact] + public void VitePlus_WithPackageManagerLaunch_ExplicitOverride_UsesExplicitValue() + { + var builder = DistributedApplication.CreateBuilder(); + + // Configure with npm but explicitly run with yarn + var vp = builder.AddVitePlusApp("vp-override") + .WithNpm() + .WithPackageManagerLaunch("yarn"); + + using var app = builder.Build(); + var appModel = app.Services.GetRequiredService(); + + var vpResource = Assert.Single(appModel.Resources.OfType()); + + // Should have configured annotation for npm + Assert.True(vpResource.TryGetLastAnnotation(out var configured)); + Assert.Equal("npm", configured.PackageManager); + + // But execution annotation should be yarn (explicit override) + Assert.True(vpResource.TryGetLastAnnotation(out var execution)); + Assert.Equal("yarn", execution.ExecutableName); + } + + [Fact] + public void VitePlus_WithPackageManagerLaunch_CalledTwiceWithSame_DoesNotThrow() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-idempotent") + .WithYarn() + .WithPackageManagerLaunch() + .WithPackageManagerLaunch(); // Second call with same inferred value + + using var app = builder.Build(); + var appModel = app.Services.GetRequiredService(); + + var vpResource = Assert.Single(appModel.Resources.OfType()); + Assert.True(vpResource.TryGetLastAnnotation(out var execution)); + Assert.Equal("yarn", execution.ExecutableName); + } + + [Theory] + [InlineData("npm")] + [InlineData("yarn")] + [InlineData("pnpm")] + [InlineData("bun")] + public async Task VitePlus_MultipleApps_AllInheritExecutionAnnotation(string packageManager) + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-multi"); + vp = packageManager switch + { + "npm" => vp.WithNpm(), + "yarn" => vp.WithYarn(), + "pnpm" => vp.WithPnpm(), + "bun" => vp.WithBun(), + _ => throw new ArgumentOutOfRangeException(nameof(packageManager)) + }; + vp = vp.WithPackageManagerLaunch(); + + vp.AddApp("app1"); + vp.AddApp("app2"); + vp.AddApp("app3"); + + using var app = builder.Build(); + var appModel = app.Services.GetRequiredService(); + + var apps = appModel.Resources.OfType().ToList(); + Assert.Equal(3, apps.Count); + + foreach (var vpApp in apps) + { + var launcherName = packageManager switch + { + "npm" => "npx", + "yarn" => "yarn", + "pnpm" => "pnpx", + "bun" => "bunx", + _ => throw new ArgumentOutOfRangeException(nameof(packageManager)) + }; + + Assert.Equal(launcherName, vpApp.Command); + var args = await vpApp.GetArgumentListAsync(); + Assert.Collection(args, + arg => Assert.Equal("vp", arg), + arg => Assert.Equal("-C", arg), + arg => Assert.Equal(Path.Combine("apps", vpApp.Name), arg), + arg => Assert.Equal("dev", arg)); + } + } + + [Fact] + public void VitePlus_WithNpm_WithoutInstallFlag_DoesNotCreateInstallerResource() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-no-installer") + .WithNpm(); // install defaults to false + + using var app = builder.Build(); + var appModel = app.Services.GetRequiredService(); + + // Should have configured annotation + var vpResource = Assert.Single(appModel.Resources.OfType()); + Assert.True(vpResource.TryGetLastAnnotation(out _)); + + // But no installer annotation or resource + Assert.False(vpResource.TryGetLastAnnotation(out _)); + Assert.Empty(appModel.Resources.OfType()); + } + + [Fact] + public void VitePlus_ConfiguredAnnotation_AddedEvenWithInstallFalse() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-configured") + .WithPnpm(install: false); + + using var app = builder.Build(); + var appModel = app.Services.GetRequiredService(); + + var vpResource = Assert.Single(appModel.Resources.OfType()); + + // Configured annotation should always be present + Assert.True(vpResource.TryGetLastAnnotation(out var configured)); + Assert.Equal("pnpm", configured.PackageManager); + + // But no installer + Assert.False(vpResource.TryGetLastAnnotation(out _)); + } + + [Fact] + public void VitePlus_WithInstallTrue_AppsWaitForInstaller() + { + var builder = DistributedApplication.CreateBuilder(); + + var vp = builder.AddVitePlusApp("vp-with-install") + .WithYarn(install: true) + .WithPackageManagerLaunch(); + + vp.AddApp("app1"); + + using var app = builder.Build(); + var appModel = app.Services.GetRequiredService(); + + // Verify installer resource exists + var vpResource = Assert.Single(appModel.Resources.OfType()); + Assert.True(vpResource.TryGetLastAnnotation(out var installerAnnotation)); + + var installer = installerAnnotation.Resource; + Assert.NotNull(installer); + Assert.Equal("vp-with-install-installer", installer.Name); + + // Verify app has WaitFor relationship + var vpApp = Assert.Single(appModel.Resources.OfType()); + Assert.True(vpApp.TryGetAnnotationsOfType(out var waitAnnotations)); + var waitAnnotation = Assert.Single(waitAnnotations); + Assert.Equal(installer, waitAnnotation.Resource); + } +}