diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..c6ac830
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,22 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v6
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+ - run: bun install --frozen-lockfile
+ - run: bun run check
+ - run: bun prettier --check .
+ - run: bun test tests/unit/ tests/e2e/
+ - run: bun run build
diff --git a/.vitepress/theme/components/HomeCTA.vue b/.vitepress/theme/components/HomeCTA.vue
index 5f52b39..35504b4 100644
--- a/.vitepress/theme/components/HomeCTA.vue
+++ b/.vitepress/theme/components/HomeCTA.vue
@@ -31,7 +31,7 @@ function copyCommand() {
Read the Docs
diff --git a/.vitepress/theme/components/HomeProductHuntBadge.vue b/.vitepress/theme/components/HomeProductHuntBadge.vue
new file mode 100644
index 0000000..94bc62c
--- /dev/null
+++ b/.vitepress/theme/components/HomeProductHuntBadge.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts
index 5bcc2a3..47c4821 100644
--- a/.vitepress/theme/index.ts
+++ b/.vitepress/theme/index.ts
@@ -1,8 +1,9 @@
// https://vitepress.dev/guide/custom-theme
-import { h } from "vue";
import type { Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
+import { h } from "vue";
import HomeCustomSections from "./components/HomeCustomSections.vue";
+import HomeProductHuntBadge from "./components/HomeProductHuntBadge.vue";
import "./style.css";
export default {
@@ -10,7 +11,10 @@ export default {
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
- "home-features-after": () => h(HomeCustomSections),
+ "home-features-after": () => [
+ h(HomeCustomSections),
+ h(HomeProductHuntBadge),
+ ],
});
},
} satisfies Theme;
diff --git a/README.md b/README.md
index 9e4b6c0..a6a6531 100644
--- a/README.md
+++ b/README.md
@@ -48,11 +48,14 @@ Or add to `package.json`:
### Options
-| Option | Default | Description |
-| --------- | ---------- | -------------------------------- |
-| `outDir` | `.srcpack` | Output directory for bundles |
-| `bundles` | — | Named bundles with glob patterns |
-| `upload` | — | Upload destination(s) |
+| Option | Default | Description |
+| ------------- | ---------- | -------------------------------------- |
+| `outDir` | `.srcpack` | Output directory for bundles |
+| `emptyOutDir` | `true`\* | Empty output directory before bundling |
+| `bundles` | — | Named bundles with glob patterns |
+| `upload` | — | Upload destination(s) |
+
+\*`emptyOutDir` defaults to `true` when `outDir` is inside project root. When `outDir` is outside root, a warning is emitted unless explicitly set.
### Bundle Config
@@ -70,7 +73,8 @@ Or add to `package.json`:
{
include: "src/**/*",
outfile: "~/Downloads/bundle.txt", // custom output path
- index: true // include index header (default)
+ index: true, // include index header (default)
+ prompt: "./prompts/review.md" // prepend from file (or inline text)
}
```
@@ -90,6 +94,7 @@ export default defineConfig({
folderId: "1ABC...", // Google Drive folder ID (from URL)
clientId: "...",
clientSecret: "...",
+ exclude: ["local"], // skip specific bundles
},
});
```
@@ -127,12 +132,14 @@ export function utils() {
## CLI
```bash
-npx srcpack # Bundle all, upload if configured
-npx srcpack web api # Bundle specific bundles only
-npx srcpack --dry-run # Preview without writing files
-npx srcpack --no-upload # Bundle only, skip upload
-npx srcpack init # Interactive config setup
-npx srcpack login # Authenticate with Google Drive
+npx srcpack # Bundle all, upload if configured
+npx srcpack web api # Bundle specific bundles only
+npx srcpack --dry-run # Preview without writing files
+npx srcpack --emptyOutDir # Empty output directory before bundling
+npx srcpack --no-emptyOutDir # Keep existing files in output directory
+npx srcpack --no-upload # Bundle only, skip upload
+npx srcpack init # Interactive config setup
+npx srcpack login # Authenticate with Google Drive
```
## API
diff --git a/bun.lock b/bun.lock
index 3532a24..f553d08 100644
--- a/bun.lock
+++ b/bun.lock
@@ -3,7 +3,7 @@
"configVersion": 1,
"workspaces": {
"": {
- "name": "rollzup",
+ "name": "srcpack",
"dependencies": {
"@clack/prompts": "^0.11.0",
"@googleapis/drive": "^20.0.0",
@@ -12,7 +12,7 @@
"google-auth-library": "^10.5.0",
"ignore": "^7.0.5",
"oauth-callback": "^1.2.5",
- "ora": "^9.0.0",
+ "ora": "^9.1.0",
"picomatch": "^4.0.2",
"zod": "^4.3.5",
},
@@ -20,7 +20,7 @@
"@types/bun": "^1.3.6",
"@types/picomatch": "^4.0.2",
"gh-pages": "^6.3.0",
- "prettier": "^3.8.0",
+ "prettier": "^3.8.1",
"typescript": "^5.9.3",
"vitepress": "^2.0.0-alpha.15",
"vitepress-plugin-llms": "^1.10.0",
@@ -28,48 +28,6 @@
},
},
"packages": {
- "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8hbezMsGa0crSt7/DKjkYL1UbbJJW/UFxTfhmf5qcIeYeeWG4dTNmm+DWbUdIsTaWvp59KC4eeC9gYXBbTHd7w=="],
-
- "@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="],
-
- "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
-
- "@ai-sdk/react": ["@ai-sdk/react@2.0.123", "", { "dependencies": { "@ai-sdk/provider-utils": "3.0.20", "ai": "5.0.121", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1", "zod": "^3.25.76 || ^4.1.8" }, "optionalPeers": ["zod"] }, "sha512-exaEvHAsDdR0wgzF3l0BmC9U1nPLnkPK2CCnX3BP4RDj/PySZvPXjry3AOz1Ayb8KSPZgWklVRzxsQxrOYQJxA=="],
-
- "@algolia/abtesting": ["@algolia/abtesting@1.12.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-0SpSdnME0RCS6UHSs9XD3ox4bMcCg1JTmjAJ3AU6rcTlX54CZOAEPc2as8uSghX6wfKGT0HWes4TeUpjJMg6FQ=="],
-
- "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.2", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", "@algolia/autocomplete-shared": "1.19.2" } }, "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw=="],
-
- "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.19.2", "", { "dependencies": { "@algolia/autocomplete-shared": "1.19.2" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg=="],
-
- "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.19.2", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w=="],
-
- "@algolia/client-abtesting": ["@algolia/client-abtesting@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-i2C8sBcl3EKXuCd5nlGohW+pZ9pY3P3JKJ2OYqsbCPg6wURiR32hNDiDvDq7/dqJ7KWWwC2snxJhokZzGlckgQ=="],
-
- "@algolia/client-analytics": ["@algolia/client-analytics@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-uFmD7m3LOym1SAURHeiqupHT9jui+9HK0lAiIvm077gXEscOM5KKXM4rg/ICzQ3UDHLZEA0Lb5TglWsXnieE6w=="],
-
- "@algolia/client-common": ["@algolia/client-common@5.46.3", "", {}, "sha512-SN+yK840nXa+2+mF72hrDfGd8+B7eBjF8TK/8KoRMdjlAkO/P3o3vtpjKRKI/Sk4L8kYYkB/avW8l+cwR+O1Ew=="],
-
- "@algolia/client-insights": ["@algolia/client-insights@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-5ic1liG0VucNPi6gKCWh5bEUGWQfyEmVeXiNKS+rOSppg7B7nKH0PEEJOFXBbHmgK5aPfNNZINiKcyUoH4XsFA=="],
-
- "@algolia/client-personalization": ["@algolia/client-personalization@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-f4HNitgTip8tntKgluYBTc1LWSOkbNCdxZvRA3rRBZnEAYSvLe7jpE+AxRep6RY+prSWwMtyeCFhA/F1Um+TuQ=="],
-
- "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-/AaVqah2aYyJj7Cazu5QRkgcV3HF3lkBJo5TRkgqQ26xR4iHNRbLF2YsWJfJpJEFghlTF2HOCh7IgzaUCnM+8A=="],
-
- "@algolia/client-search": ["@algolia/client-search@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-hfpCIukPuwkrlwsYfJEWdU5R5bduBHEq2uuPcqmgPgNq5MSjmiNIzRuzxGZZgiBKcre6gZT00DR7G1AFn//wiQ=="],
-
- "@algolia/ingestion": ["@algolia/ingestion@1.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-ChVzNkCzAVxKozTnTgPWCG69WQLjzW7X6OqD91zUh8U38ZhPEX/t3qGhXs+M9ZNaHcJ7xToMB3jywNwONhpLGA=="],
-
- "@algolia/monitoring": ["@algolia/monitoring@1.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-MZa+Z5iPmVMxVAQ0aq4HpGsja5utSLEMcOuY01X8D46vvMrSPkP8DnlDFtu1PgJ0RwyIGqqx7v+ClFo6iRJ6bA=="],
-
- "@algolia/recommend": ["@algolia/recommend@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-cr3atJRJBKgAKZl/Oxo4sig6Se0+ukbyIOOluPV5H+ZAXVcxuMoXQgwQ1M5UHPnCnEsZ4uBXhBmilRgUQpUegw=="],
-
- "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3" } }, "sha512-/Ku9GImJf2SKoRM2S3e03MjCVaWJCP5olih4k54DRhNDdmxBkd3nsWuUXvDElY3Ucw/arBYGs5SYz79SoS5APw=="],
-
- "@algolia/requester-fetch": ["@algolia/requester-fetch@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3" } }, "sha512-Uw+SPy/zpfwbH1AxQaeOWvWVzPEcO0XbtLbbSz0HPcEIiBGWyfa9LUCxD5UferbDjrSQNVimmzl3FaWi4u8Ykw=="],
-
- "@algolia/requester-node-http": ["@algolia/requester-node-http@5.46.3", "", { "dependencies": { "@algolia/client-common": "5.46.3" } }, "sha512-4No9iTjr1GZ0JWsFbQJj9aZBnmKyY1sTxOoEud9+SGe3U6iAulF0A0lI4cWi/F/Gcfg8V3jkaddcqSQKDnE45w=="],
-
"@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
@@ -84,13 +42,9 @@
"@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
- "@docsearch/core": ["@docsearch/core@4.4.0", "", { "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", "react": ">= 16.8.0 < 20.0.0", "react-dom": ">= 16.8.0 < 20.0.0" }, "optionalPeers": ["@types/react", "react", "react-dom"] }, "sha512-kiwNo5KEndOnrf5Kq/e5+D9NBMCFgNsDoRpKQJ9o/xnSlheh6b8AXppMuuUVVdAUIhIfQFk/07VLjjk/fYyKmw=="],
-
- "@docsearch/css": ["@docsearch/css@4.4.0", "", {}, "sha512-e9vPgtih6fkawakmYo0Y6V4BKBmDV7Ykudn7ADWXUs5b6pmtBRwDbpSG/WiaUG63G28OkJDEnsMvgIAnZgGwYw=="],
+ "@docsearch/css": ["@docsearch/css@4.5.3", "", {}, "sha512-kUpHaxn0AgI3LQfyzTYkNUuaFY4uEz/Ym9/N/FvyDE+PzSgZsCyDH9jE49B6N6f1eLCm9Yp64J9wENd6vypdxA=="],
- "@docsearch/js": ["@docsearch/js@4.4.0", "", { "dependencies": { "@docsearch/react": "4.4.0", "htm": "3.1.1" } }, "sha512-vCiKzjYD54bugUIMZA6YzuLDilkD3TNH/kfbvqsnzxiLTMu8F13psD+hdMSEOn7j+dFJOaf49fZ+gwr+rXctMw=="],
-
- "@docsearch/react": ["@docsearch/react@4.4.0", "", { "dependencies": { "@ai-sdk/react": "^2.0.30", "@algolia/autocomplete-core": "1.19.2", "@docsearch/core": "4.4.0", "@docsearch/css": "4.4.0", "ai": "^5.0.30", "algoliasearch": "^5.28.0", "marked": "^16.3.0", "zod": "^4.1.8" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", "react": ">= 16.8.0 < 20.0.0", "react-dom": ">= 16.8.0 < 20.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-z12zeg1mV7WD4Ag4pKSuGukETJLaucVFwszDXL/qLaEgRqxEaVacO9SR1qqnCXvZztlvz2rt7cMqryi/7sKfjA=="],
+ "@docsearch/js": ["@docsearch/js@4.5.3", "", {}, "sha512-rcBiUMCXbZLqrLIT6F6FDcrG/tyvM2WM0zum6NPbIiQNDQxbSgmNc+/bToS0rxBsXaxiU64esiWoS02WqrWLsg=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
@@ -146,7 +100,7 @@
"@googleapis/drive": ["@googleapis/drive@20.0.0", "", { "dependencies": { "googleapis-common": "^8.0.0" } }, "sha512-qLi5ypZn0zYY2FcGjdlHQsv1DAFNRwCWFiE5kq23J0yTdUSZynh/mDph9NBaiQ9ybajrmttySR/rSaNfm8S/bA=="],
- "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.66", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-D1OnnXwiQXFkVMw5M+Bt8mPsXeMkQyGmMdrmN7lsQlKMUkfLOp6JWhnUJ92po51WXT046aF/zzqSmkKqg08p4Q=="],
+ "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.67", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-RGJRwlxyup54L1UDAjCshy3ckX5zcvYIU74YLSnUgHGvqh6B4mvksbGNHAIEp7dZQ6cM13RZVT5KC07CmnFNew=="],
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
@@ -164,61 +118,59 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
- "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
-
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.53", "", {}, "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ=="],
- "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="],
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.56.0", "", { "os": "android", "cpu": "arm" }, "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw=="],
- "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="],
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.56.0", "", { "os": "android", "cpu": "arm64" }, "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q=="],
- "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="],
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.56.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w=="],
- "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="],
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.56.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g=="],
- "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="],
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.56.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ=="],
- "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="],
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.56.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg=="],
- "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="],
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.56.0", "", { "os": "linux", "cpu": "arm" }, "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A=="],
- "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="],
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.56.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw=="],
- "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="],
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.56.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ=="],
- "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="],
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.56.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA=="],
- "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="],
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg=="],
- "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="],
+ "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA=="],
- "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="],
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.56.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw=="],
- "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="],
+ "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.56.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg=="],
- "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="],
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew=="],
- "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="],
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.56.0", "", { "os": "linux", "cpu": "none" }, "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ=="],
- "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="],
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.56.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ=="],
- "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="],
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.56.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw=="],
- "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="],
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.56.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA=="],
- "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="],
+ "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.56.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA=="],
- "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="],
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.56.0", "", { "os": "none", "cpu": "arm64" }, "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ=="],
- "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="],
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.56.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing=="],
- "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="],
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.56.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg=="],
- "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="],
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.56.0", "", { "os": "win32", "cpu": "x64" }, "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ=="],
- "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="],
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.56.0", "", { "os": "win32", "cpu": "x64" }, "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g=="],
"@shikijs/core": ["@shikijs/core@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA=="],
@@ -236,8 +188,6 @@
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
- "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
-
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
@@ -256,7 +206,7 @@
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
- "@types/node": ["@types/node@25.0.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw=="],
+ "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
"@types/picomatch": ["@types/picomatch@4.0.2", "", {}, "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA=="],
@@ -266,17 +216,15 @@
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
- "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="],
-
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.3", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.53" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "vue": "^3.2.25" } }, "sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w=="],
- "@vue/compiler-core": ["@vue/compiler-core@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.26", "entities": "^7.0.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w=="],
+ "@vue/compiler-core": ["@vue/compiler-core@3.5.27", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.27", "entities": "^7.0.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ=="],
- "@vue/compiler-dom": ["@vue/compiler-dom@3.5.26", "", { "dependencies": { "@vue/compiler-core": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A=="],
+ "@vue/compiler-dom": ["@vue/compiler-dom@3.5.27", "", { "dependencies": { "@vue/compiler-core": "3.5.27", "@vue/shared": "3.5.27" } }, "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w=="],
- "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/compiler-core": "3.5.26", "@vue/compiler-dom": "3.5.26", "@vue/compiler-ssr": "3.5.26", "@vue/shared": "3.5.26", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA=="],
+ "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.27", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/compiler-core": "3.5.27", "@vue/compiler-dom": "3.5.27", "@vue/compiler-ssr": "3.5.27", "@vue/shared": "3.5.27", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ=="],
- "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.26", "", { "dependencies": { "@vue/compiler-dom": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw=="],
+ "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.27", "", { "dependencies": { "@vue/compiler-dom": "3.5.27", "@vue/shared": "3.5.27" } }, "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw=="],
"@vue/devtools-api": ["@vue/devtools-api@8.0.5", "", { "dependencies": { "@vue/devtools-kit": "^8.0.5" } }, "sha512-DgVcW8H/Nral7LgZEecYFFYXnAvGuN9C3L3DtWekAncFBedBczpNW8iHKExfaM559Zm8wQWrwtYZ9lXthEHtDw=="],
@@ -284,15 +232,15 @@
"@vue/devtools-shared": ["@vue/devtools-shared@8.0.5", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg=="],
- "@vue/reactivity": ["@vue/reactivity@3.5.26", "", { "dependencies": { "@vue/shared": "3.5.26" } }, "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ=="],
+ "@vue/reactivity": ["@vue/reactivity@3.5.27", "", { "dependencies": { "@vue/shared": "3.5.27" } }, "sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ=="],
- "@vue/runtime-core": ["@vue/runtime-core@3.5.26", "", { "dependencies": { "@vue/reactivity": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q=="],
+ "@vue/runtime-core": ["@vue/runtime-core@3.5.27", "", { "dependencies": { "@vue/reactivity": "3.5.27", "@vue/shared": "3.5.27" } }, "sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A=="],
- "@vue/runtime-dom": ["@vue/runtime-dom@3.5.26", "", { "dependencies": { "@vue/reactivity": "3.5.26", "@vue/runtime-core": "3.5.26", "@vue/shared": "3.5.26", "csstype": "^3.2.3" } }, "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ=="],
+ "@vue/runtime-dom": ["@vue/runtime-dom@3.5.27", "", { "dependencies": { "@vue/reactivity": "3.5.27", "@vue/runtime-core": "3.5.27", "@vue/shared": "3.5.27", "csstype": "^3.2.3" } }, "sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg=="],
- "@vue/server-renderer": ["@vue/server-renderer@3.5.26", "", { "dependencies": { "@vue/compiler-ssr": "3.5.26", "@vue/shared": "3.5.26" }, "peerDependencies": { "vue": "3.5.26" } }, "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA=="],
+ "@vue/server-renderer": ["@vue/server-renderer@3.5.27", "", { "dependencies": { "@vue/compiler-ssr": "3.5.27", "@vue/shared": "3.5.27" }, "peerDependencies": { "vue": "3.5.27" } }, "sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA=="],
- "@vue/shared": ["@vue/shared@3.5.26", "", {}, "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A=="],
+ "@vue/shared": ["@vue/shared@3.5.27", "", {}, "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ=="],
"@vueuse/core": ["@vueuse/core@14.1.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.1.0", "@vueuse/shared": "14.1.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw=="],
@@ -304,10 +252,6 @@
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
- "ai": ["ai@5.0.121", "", { "dependencies": { "@ai-sdk/gateway": "2.0.27", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-3iYPdARKGLryC/7OA9RgBUaym1gynvWS7UPy8NwoRNCKP52lshldtHB5xcEfVviw7liWH2zJlW9yEzsDglcIEQ=="],
-
- "algoliasearch": ["algoliasearch@5.46.3", "", { "dependencies": { "@algolia/abtesting": "1.12.3", "@algolia/client-abtesting": "5.46.3", "@algolia/client-analytics": "5.46.3", "@algolia/client-common": "5.46.3", "@algolia/client-insights": "5.46.3", "@algolia/client-personalization": "5.46.3", "@algolia/client-query-suggestions": "5.46.3", "@algolia/client-search": "5.46.3", "@algolia/ingestion": "1.46.3", "@algolia/monitoring": "1.46.3", "@algolia/recommend": "5.46.3", "@algolia/requester-browser-xhr": "5.46.3", "@algolia/requester-fetch": "5.46.3", "@algolia/requester-node-http": "5.46.3" } }, "sha512-n/NdPglzmkcNYZfIT3Fo8pnDR/lKiK1kZ1Yaa315UoLyHymADhWw15+bzN5gBxrCA8KyeNu0JJD6mLtTov43lQ=="],
-
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
@@ -382,7 +326,7 @@
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
- "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
+ "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
"default-browser": ["default-browser@5.4.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg=="],
@@ -428,8 +372,6 @@
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
- "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
-
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
"extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
@@ -512,8 +454,6 @@
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
- "htm": ["htm@3.1.1", "", {}, "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ=="],
-
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
@@ -562,8 +502,6 @@
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
- "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
-
"jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
@@ -594,8 +532,6 @@
"markdown-title": ["markdown-title@1.0.2", "", {}, "sha512-MqIQVVkz+uGEHi3TsHx/czcxxCbRIL7sv5K5DnYw/tI+apY54IbPefV/cmgxp6LoJSEx/TqcHdLs/298afG5QQ=="],
- "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
-
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
@@ -692,7 +628,7 @@
"open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
- "ora": ["ora@9.0.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", "string-width": "^8.1.0", "strip-ansi": "^7.1.2" } }, "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A=="],
+ "ora": ["ora@9.1.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", "string-width": "^8.1.0" } }, "sha512-53uuLsXHOAJl5zLrUrzY9/kE+uIFEx7iaH4g2BIJQK4LZjY4LpCCYZVKDWIkL+F01wAaCg93duQ1whnK/AmY1A=="],
"p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
@@ -716,7 +652,7 @@
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
- "perfect-debounce": ["perfect-debounce@2.0.0", "", {}, "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow=="],
+ "perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
@@ -728,7 +664,7 @@
"powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="],
- "prettier": ["prettier@3.8.0", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA=="],
+ "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
"pretty-bytes": ["pretty-bytes@7.1.0", "", {}, "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw=="],
@@ -740,8 +676,6 @@
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
- "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
-
"regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
"regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
@@ -768,7 +702,7 @@
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
- "rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="],
+ "rollup": ["rollup@4.56.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.56.0", "@rollup/rollup-android-arm64": "4.56.0", "@rollup/rollup-darwin-arm64": "4.56.0", "@rollup/rollup-darwin-x64": "4.56.0", "@rollup/rollup-freebsd-arm64": "4.56.0", "@rollup/rollup-freebsd-x64": "4.56.0", "@rollup/rollup-linux-arm-gnueabihf": "4.56.0", "@rollup/rollup-linux-arm-musleabihf": "4.56.0", "@rollup/rollup-linux-arm64-gnu": "4.56.0", "@rollup/rollup-linux-arm64-musl": "4.56.0", "@rollup/rollup-linux-loong64-gnu": "4.56.0", "@rollup/rollup-linux-loong64-musl": "4.56.0", "@rollup/rollup-linux-ppc64-gnu": "4.56.0", "@rollup/rollup-linux-ppc64-musl": "4.56.0", "@rollup/rollup-linux-riscv64-gnu": "4.56.0", "@rollup/rollup-linux-riscv64-musl": "4.56.0", "@rollup/rollup-linux-s390x-gnu": "4.56.0", "@rollup/rollup-linux-x64-gnu": "4.56.0", "@rollup/rollup-linux-x64-musl": "4.56.0", "@rollup/rollup-openbsd-x64": "4.56.0", "@rollup/rollup-openharmony-arm64": "4.56.0", "@rollup/rollup-win32-arm64-msvc": "4.56.0", "@rollup/rollup-win32-ia32-msvc": "4.56.0", "@rollup/rollup-win32-x64-gnu": "4.56.0", "@rollup/rollup-win32-x64-msvc": "4.56.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg=="],
"run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
@@ -776,8 +710,6 @@
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
- "search-insights": ["search-insights@2.17.3", "", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="],
-
"section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
@@ -828,12 +760,8 @@
"superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
- "swr": ["swr@2.3.8", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w=="],
-
"tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
- "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="],
-
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
@@ -870,8 +798,6 @@
"url-template": ["url-template@2.0.8", "", {}, "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="],
- "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
-
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
@@ -882,7 +808,7 @@
"vitepress-plugin-llms": ["vitepress-plugin-llms@1.10.0", "", { "dependencies": { "gray-matter": "^4.0.3", "markdown-it": "^14.1.0", "markdown-title": "^1.0.2", "mdast-util-from-markdown": "^2.0.2", "millify": "^6.1.0", "minimatch": "^10.1.1", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "pretty-bytes": "^7.1.0", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "tokenx": "^1.2.1", "unist-util-remove": "^4.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-dgD5KV8D9vXlQtAf/KUjSgr3QymH1fHT7XkQ/UuIqvIjnKdzZI+0gT3puGxUBuqgvlFjYWA6f8k80tXl6gwWkw=="],
- "vue": ["vue@3.5.26", "", { "dependencies": { "@vue/compiler-dom": "3.5.26", "@vue/compiler-sfc": "3.5.26", "@vue/runtime-dom": "3.5.26", "@vue/server-renderer": "3.5.26", "@vue/shared": "3.5.26" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA=="],
+ "vue": ["vue@3.5.27", "", { "dependencies": { "@vue/compiler-dom": "3.5.27", "@vue/compiler-sfc": "3.5.27", "@vue/runtime-dom": "3.5.27", "@vue/server-renderer": "3.5.27", "@vue/shared": "3.5.27" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw=="],
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
@@ -910,7 +836,7 @@
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
- "@vue/compiler-core/entities": ["entities@7.0.0", "", {}, "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ=="],
+ "@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
"cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
diff --git a/docs/configuration.md b/docs/configuration.md
index 4e37757..37f7838 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -24,11 +24,28 @@ export default defineConfig({
## Options
-| Option | Type | Default | Description |
-| --------- | -------- | ---------- | ---------------------------- |
-| `outDir` | `string` | `.srcpack` | Output directory for bundles |
-| `bundles` | `object` | — | Named bundles (required) |
-| `upload` | `object` | — | Upload destination |
+| Option | Type | Default | Description |
+| ------------- | --------- | --------------- | -------------------------------------- |
+| `root` | `string` | `process.cwd()` | Project root directory |
+| `outDir` | `string` | `.srcpack` | Output directory for bundles |
+| `emptyOutDir` | `boolean` | `true`\* | Empty output directory before bundling |
+| `bundles` | `object` | — | Named bundles (required) |
+| `upload` | `object` | — | Upload destination |
+
+\*`emptyOutDir` defaults to `true` when `outDir` is inside project root.
+
+### root
+
+Project root directory where files are bundled from. Can be absolute or relative to CWD.
+
+```ts
+export default defineConfig({
+ root: "./packages/app", // bundle from subdirectory
+ bundles: {
+ app: "src/**/*", // matches packages/app/src/**/*
+ },
+});
+```
## Bundle Definitions
@@ -66,11 +83,12 @@ bundles: {
**Bundle options:**
-| Option | Type | Default | Description |
-| --------- | -------------------- | --------------------- | -------------------- |
-| `include` | `string \| string[]` | — | Glob pattern(s) |
-| `outfile` | `string` | `{outDir}/{name}.txt` | Custom output path |
-| `index` | `boolean` | `true` | Include index header |
+| Option | Type | Default | Description |
+| --------- | -------------------- | --------------------- | ----------------------------------------- |
+| `include` | `string \| string[]` | — | Glob pattern(s) |
+| `outfile` | `string` | `{outDir}/{name}.txt` | Custom output path |
+| `index` | `boolean` | `true` | Include index header |
+| `prompt` | `string` | — | Text or file path (`./`, `~/`) to prepend |
## Pattern Syntax
@@ -148,6 +166,19 @@ export default defineConfig({
});
```
+### Code Review Bundle
+
+```ts
+export default defineConfig({
+ bundles: {
+ review: {
+ include: "src/**/*",
+ prompt: "./prompts/review.md", // or inline: "Review this code..."
+ },
+ },
+});
+```
+
### Package.json Config
```json
@@ -160,6 +191,35 @@ export default defineConfig({
}
```
+## Upload Configuration
+
+Configure cloud upload destinations. See [Google Drive Upload](/upload) for setup details.
+
+```ts
+export default defineConfig({
+ bundles: {
+ /* ... */
+ },
+ upload: {
+ provider: "gdrive",
+ folderId: "1ABC...",
+ clientId: process.env.GDRIVE_CLIENT_ID,
+ clientSecret: process.env.GDRIVE_CLIENT_SECRET,
+ exclude: ["local"], // skip these bundles
+ },
+});
+```
+
+**Upload options:**
+
+| Option | Type | Default | Description |
+| -------------- | ---------- | ------- | ---------------------------------- |
+| `provider` | `"gdrive"` | — | Upload provider (required) |
+| `folderId` | `string` | — | Target folder ID (optional) |
+| `clientId` | `string` | — | OAuth client ID (required) |
+| `clientSecret` | `string` | — | OAuth client secret (required) |
+| `exclude` | `string[]` | — | Bundle names to skip during upload |
+
## TypeScript Support
The `defineConfig` helper provides type checking and autocomplete:
diff --git a/docs/upload.md b/docs/upload.md
index c0199ab..31b0446 100644
--- a/docs/upload.md
+++ b/docs/upload.md
@@ -24,16 +24,28 @@ export default defineConfig({
bundles: {
web: "apps/web/**/*",
api: "apps/api/**/*",
+ local: "local/**/*", // local-only bundle
},
upload: {
provider: "gdrive",
folderId: "1ABC...", // From Drive folder URL
clientId: "...",
clientSecret: "...",
+ exclude: ["local"], // skip these bundles
},
});
```
+**Upload options:**
+
+| Option | Type | Description |
+| -------------- | ---------- | ------------------------------------------------ |
+| `provider` | `"gdrive"` | Upload provider (currently only gdrive) |
+| `folderId` | `string` | Google Drive folder ID (optional, defaults root) |
+| `clientId` | `string` | OAuth 2.0 client ID |
+| `clientSecret` | `string` | OAuth 2.0 client secret |
+| `exclude` | `string[]` | Bundle names to skip during upload |
+
**Finding your folder ID:**
Open the target folder in Google Drive. The URL looks like:
@@ -80,6 +92,21 @@ Once configured, `npx srcpack` uploads bundles after bundling:
↑ Uploaded to Google Drive
```
+### Exclude Bundles
+
+To skip specific bundles from upload, use the `exclude` option:
+
+```ts
+upload: {
+ provider: "gdrive",
+ clientId: "...",
+ clientSecret: "...",
+ exclude: ["local", "debug"], // these bundles won't upload
+}
+```
+
+This is useful for local-only bundles that shouldn't be shared.
+
### Skip Upload
To bundle without uploading:
diff --git a/package.json b/package.json
index 07caee5..a273a6f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "srcpack",
- "version": "0.1.6",
+ "version": "0.1.10",
"description": "Zero-config CLI for bundling code into LLM-optimized context files",
"keywords": [
"llm",
@@ -79,7 +79,7 @@
"google-auth-library": "^10.5.0",
"ignore": "^7.0.5",
"oauth-callback": "^1.2.5",
- "ora": "^9.0.0",
+ "ora": "^9.1.0",
"picomatch": "^4.0.2",
"zod": "^4.3.5"
},
@@ -87,7 +87,7 @@
"@types/bun": "^1.3.6",
"@types/picomatch": "^4.0.2",
"gh-pages": "^6.3.0",
- "prettier": "^3.8.0",
+ "prettier": "^3.8.1",
"typescript": "^5.9.3",
"vitepress": "^2.0.0-alpha.15",
"vitepress-plugin-llms": "^1.10.0"
diff --git a/src/bundle.ts b/src/bundle.ts
index 8614cbe..94857a4 100644
--- a/src/bundle.ts
+++ b/src/bundle.ts
@@ -5,7 +5,7 @@ import { join } from "node:path";
import { glob } from "fast-glob";
import picomatch from "picomatch";
import ignore, { type Ignore } from "ignore";
-import type { BundleConfigInput } from "./config.ts";
+import { expandPath, type BundleConfigInput } from "./config.ts";
// Binary file detection: check first 8KB for null bytes (same heuristic as git)
const BINARY_CHECK_SIZE = 8192;
@@ -86,20 +86,79 @@ function isExcluded(filePath: string, matchers: Matcher[]): boolean {
}
/**
- * Load and parse .gitignore file from a directory
+ * Convert gitignore patterns to glob ignore patterns for fast-glob.
+ * This prevents traversing into ignored directories (performance optimization).
+ *
+ * Conservative approach: only convert simple, unambiguous directory patterns.
+ * Complex patterns (negations, root-anchored, globs) are left to the ignore filter.
*/
-async function loadGitignore(cwd: string): Promise {
+function gitignoreToGlobPatterns(lines: string[]): string[] {
+ // If any negation patterns exist, skip optimization entirely
+ // (negations could re-include files in otherwise-ignored directories)
+ const hasNegation = lines.some((line) => {
+ const trimmed = line.trim();
+ // Any line starting with ! is a negation (including !#file which negates "#file")
+ return trimmed.startsWith("!");
+ });
+ if (hasNegation) return [];
+
+ const patterns: string[] = [];
+
+ for (const line of lines) {
+ const trimmed = line.trim();
+ // Skip empty lines and comments
+ if (!trimmed || trimmed.startsWith("#")) continue;
+
+ // Skip patterns with special gitignore features we can't safely convert:
+ // - Root-anchored (starts with /)
+ // - Contains globs (*, ?, [)
+ // - Contains path separators (complex paths)
+ // - Escaped characters
+ if (
+ trimmed.startsWith("/") ||
+ trimmed.includes("*") ||
+ trimmed.includes("?") ||
+ trimmed.includes("[") ||
+ trimmed.includes("/") ||
+ trimmed.includes("\\")
+ ) {
+ continue;
+ }
+
+ // Only convert simple directory names (e.g., "node_modules", "dist")
+ // These are safe to prune at any depth
+ const name = trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
+ if (name && /^[\w.-]+$/.test(name)) {
+ patterns.push(`**/${name}/**`);
+ }
+ }
+
+ return patterns;
+}
+
+interface GitignoreResult {
+ ignore: Ignore;
+ globPatterns: string[];
+}
+
+/**
+ * Load and parse .gitignore file from a directory.
+ * Returns both an Ignore instance for filtering and glob patterns for fast-glob.
+ */
+async function loadGitignore(cwd: string): Promise {
const ig = ignore();
const gitignorePath = join(cwd, ".gitignore");
+ let globPatterns: string[] = [];
try {
const content = await readFile(gitignorePath, "utf-8");
ig.add(content);
+ globPatterns = gitignoreToGlobPatterns(content.split("\n"));
} catch {
// No .gitignore file, return empty ignore instance
}
- return ig;
+ return { ignore: ig, globPatterns };
}
/**
@@ -114,12 +173,18 @@ export async function resolvePatterns(
): Promise {
const { include, exclude, force } = normalizePatterns(config);
const excludeMatchers = exclude.map((p) => picomatch(p));
- const gitignore = await loadGitignore(cwd);
+ const { ignore: gitignore, globPatterns } = await loadGitignore(cwd);
const files = new Set();
// Regular includes: respect .gitignore
+ // Pass gitignore patterns to fast-glob to skip ignored directories during traversal
if (include.length > 0) {
- const matches = await glob(include, { cwd, onlyFiles: true, dot: true });
+ const matches = await glob(include, {
+ cwd,
+ onlyFiles: true,
+ dot: true,
+ ignore: globPatterns,
+ });
for (const match of matches) {
if (!isExcluded(match, excludeMatchers) && !gitignore.ignores(match)) {
const fullPath = join(cwd, match);
@@ -130,7 +195,7 @@ export async function resolvePatterns(
}
}
- // Force includes: bypass .gitignore
+ // Force includes: bypass .gitignore (no ignore patterns passed to glob)
if (force.length > 0) {
const matches = await glob(force, { cwd, onlyFiles: true, dot: true });
for (const match of matches) {
@@ -182,6 +247,7 @@ export function formatIndex(index: FileEntry[]): string {
export interface BundleOptions {
includeIndex?: boolean; // Default: true
+ prompt?: string; // Text to prepend to bundle
}
/**
@@ -204,6 +270,8 @@ export async function createBundle(
options: BundleOptions = {},
): Promise {
const { includeIndex = true } = options;
+ // Normalize prompt: trim and treat whitespace-only as no prompt
+ const prompt = options.prompt?.trim() || undefined;
const index: FileEntry[] = [];
const contentParts: string[] = [];
let currentLine = 1;
@@ -232,26 +300,44 @@ export async function createBundle(
currentLine = entry.endLine + 1;
}
+ // Calculate prompt offset (prompt text + blank + "---" + blank)
+ const promptLines = prompt ? countLines(prompt) + 3 : 0;
+
if (includeIndex) {
// Adjust line numbers to account for index header
// Header: "# Index (N files)" + N index lines + 1 blank line
- const headerLines = index.length + 2;
+ const headerLines = index.length + 2 + promptLines;
for (const entry of index) {
entry.startLine += headerLines;
entry.endLine += headerLines;
}
const indexBlock = formatIndex(index);
- const content =
+ const bundleContent =
index.length === 0
? indexBlock
: indexBlock + "\n\n" + contentParts.join("\n");
+ const content = prompt
+ ? `${prompt}\n\n---\n\n${bundleContent}`
+ : bundleContent;
return { content, index };
}
// No index: just join file content
- const content = contentParts.join("\n");
+ const bundleContent = contentParts.join("\n");
+ const content = prompt
+ ? `${prompt}\n\n---\n\n${bundleContent}`
+ : bundleContent;
+
+ // Adjust line numbers for prompt offset (no index case)
+ if (promptLines > 0) {
+ for (const entry of index) {
+ entry.startLine += promptLines;
+ entry.endLine += promptLines;
+ }
+ }
+
return { content, index };
}
@@ -265,6 +351,46 @@ function getIncludeIndex(config: BundleConfigInput): boolean {
return true;
}
+/**
+ * Extract the prompt option from bundle config.
+ * Returns undefined for empty/null/undefined values.
+ */
+function getPrompt(config: BundleConfigInput): string | undefined {
+ if (typeof config === "object" && !Array.isArray(config)) {
+ const prompt = config.prompt;
+ // Treat empty string, null, undefined as no prompt
+ return prompt && prompt.trim() ? prompt : undefined;
+ }
+ return undefined;
+}
+
+/**
+ * Resolve prompt value: load from file if path, otherwise return as-is.
+ * Paths starting with ./, ../, or ~/ are treated as file paths.
+ */
+async function resolvePrompt(
+ prompt: string | undefined,
+ cwd: string,
+): Promise {
+ if (!prompt) return undefined;
+
+ // Check if prompt looks like a file path
+ if (prompt.startsWith("./") || prompt.startsWith("../")) {
+ const filePath = join(cwd, prompt);
+ const content = await readFile(filePath, "utf-8");
+ return content.trim() || undefined;
+ }
+
+ if (prompt.startsWith("~/")) {
+ const filePath = expandPath(prompt);
+ const content = await readFile(filePath, "utf-8");
+ return content.trim() || undefined;
+ }
+
+ // Trim inline prompts for consistent behavior with file-based prompts
+ return prompt.trim() || undefined;
+}
+
/**
* Bundle a single named bundle from config
*/
@@ -275,5 +401,6 @@ export async function bundleOne(
): Promise {
const files = await resolvePatterns(config, cwd);
const includeIndex = getIncludeIndex(config);
- return createBundle(files, cwd, { includeIndex });
+ const prompt = await resolvePrompt(getPrompt(config), cwd);
+ return createBundle(files, cwd, { includeIndex, prompt });
}
diff --git a/src/cli.ts b/src/cli.ts
index 29d5484..bfd9a07 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -1,8 +1,8 @@
#!/usr/bin/env node
// SPDX-License-Identifier: MIT
-import { mkdir, writeFile } from "node:fs/promises";
-import { dirname, join } from "node:path";
+import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
+import { dirname, isAbsolute, join, relative, resolve } from "node:path";
import ora from "ora";
import { bundleOne, type BundleResult } from "./bundle.ts";
import {
@@ -38,6 +38,31 @@ function plural(n: number, singular: string, pluralForm?: string): string {
return n === 1 ? singular : (pluralForm ?? singular + "s");
}
+function isOutDirInsideRoot(outDir: string, root: string): boolean {
+ const absoluteOutDir = isAbsolute(outDir) ? outDir : resolve(root, outDir);
+ const rel = relative(root, absoluteOutDir);
+ return !rel.startsWith("..") && !isAbsolute(rel);
+}
+
+/**
+ * Empty a directory while preserving specified entries (e.g., `.git`).
+ * Uses `force: true` to handle read-only or in-use files.
+ */
+async function emptyDirectory(dir: string, skip: string[] = []): Promise {
+ let entries: string[];
+ try {
+ entries = await readdir(dir);
+ } catch {
+ return; // Directory doesn't exist, nothing to empty
+ }
+ const skipSet = new Set(skip);
+ await Promise.all(
+ entries
+ .filter((entry) => !skipSet.has(entry))
+ .map((entry) => rm(join(dir, entry), { recursive: true, force: true })),
+ );
+}
+
async function main() {
const args = process.argv.slice(2);
@@ -54,9 +79,11 @@ Usage:
npx srcpack login Authenticate with Google Drive
Options:
- --dry-run Preview bundles without writing files
- --no-upload Skip uploading to cloud storage
- -h, --help Show this help message
+ --dry-run Preview bundles without writing files
+ --emptyOutDir Empty output directory before bundling
+ --no-emptyOutDir Keep existing files in output directory
+ --no-upload Skip uploading to cloud storage
+ -h, --help Show this help message
`);
return;
}
@@ -73,6 +100,12 @@ Options:
const dryRun = args.includes("--dry-run");
const noUpload = args.includes("--no-upload");
+ // CLI flags: --emptyOutDir forces true, --no-emptyOutDir forces false
+ const emptyOutDirFlag = args.includes("--emptyOutDir")
+ ? true
+ : args.includes("--no-emptyOutDir")
+ ? false
+ : undefined;
const subcommands = ["init", "login"];
const requestedBundles = args.filter(
(arg) => !arg.startsWith("-") && !subcommands.includes(arg),
@@ -105,7 +138,32 @@ Options:
return;
}
- const cwd = process.cwd();
+ const root = config.root;
+
+ // Resolve emptyOutDir: CLI flag > config > auto (true if inside root)
+ const outDirInsideRoot = isOutDirInsideRoot(config.outDir, root);
+ const emptyOutDir = emptyOutDirFlag ?? config.emptyOutDir ?? outDirInsideRoot;
+
+ // Warn if outDir is outside root and emptyOutDir is not explicitly set
+ if (
+ !outDirInsideRoot &&
+ emptyOutDirFlag === undefined &&
+ config.emptyOutDir === undefined
+ ) {
+ console.warn(
+ `Warning: outDir "${config.outDir}" is outside project root. ` +
+ "Use --emptyOutDir to suppress this warning and empty the directory.",
+ );
+ }
+
+ // Empty outDir before bundling (unless dry-run)
+ if (emptyOutDir && !dryRun) {
+ const outDirPath = isAbsolute(config.outDir)
+ ? config.outDir
+ : resolve(root, config.outDir);
+ await emptyDirectory(outDirPath, [".git"]);
+ }
+
const outputs: BundleOutput[] = [];
// Process all bundles with progress
@@ -118,7 +176,7 @@ Options:
const name = bundleNames[i]!;
bundleSpinner.text = `Bundling ${name}... (${i + 1}/${bundleNames.length})`;
const bundleConfig = config.bundles[name]!;
- const result = await bundleOne(name, bundleConfig, cwd);
+ const result = await bundleOne(name, bundleConfig, root);
const outfile = getOutfile(bundleConfig, name, config.outDir);
outputs.push({ name, outfile, result });
}
@@ -139,7 +197,7 @@ Options:
for (const { name, outfile, result } of outputs) {
const fileCount = result.index.length;
const lineCount = sumLines(result);
- const outPath = join(cwd, outfile);
+ const outPath = join(root, outfile);
const nameCol = name.padEnd(maxNameLen);
const filesCol = formatNumber(fileCount).padStart(maxFilesLen);
@@ -186,7 +244,7 @@ Options:
for (const uploadConfig of uploads) {
if (isGdriveConfigured(uploadConfig)) {
- await handleGdriveUpload(uploadConfig, outputs, cwd);
+ await handleGdriveUpload(uploadConfig, outputs, root);
}
}
}
@@ -277,8 +335,17 @@ function printUploadConfigHelp(): void {
async function handleGdriveUpload(
uploadConfig: UploadConfig,
outputs: BundleOutput[],
- cwd: string,
+ root: string,
): Promise {
+ // Filter out excluded bundles
+ const excludeSet = new Set(uploadConfig.exclude ?? []);
+ const toUpload = outputs.filter((o) => !excludeSet.has(o.name));
+
+ if (toUpload.length === 0) {
+ console.log("\nNo bundles to upload (all excluded).");
+ return;
+ }
+
try {
await ensureAuthenticated(uploadConfig);
@@ -289,10 +356,10 @@ async function handleGdriveUpload(
const results: UploadResult[] = [];
- for (let i = 0; i < outputs.length; i++) {
- const output = outputs[i]!;
- const filePath = join(cwd, output.outfile);
- uploadSpinner.text = `Uploading ${output.name}... (${i + 1}/${outputs.length})`;
+ for (let i = 0; i < toUpload.length; i++) {
+ const output = toUpload[i]!;
+ const filePath = join(root, output.outfile);
+ uploadSpinner.text = `Uploading ${output.name}... (${i + 1}/${toUpload.length})`;
const result = await uploadFile(filePath, uploadConfig);
results.push(result);
}
diff --git a/src/config.ts b/src/config.ts
index 75a61cb..097d5be 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
import { homedir } from "node:os";
-import { join } from "node:path";
+import { join, resolve } from "node:path";
import { cosmiconfig } from "cosmiconfig";
import { z } from "zod";
@@ -12,33 +12,75 @@ export function expandPath(p: string): string {
return p;
}
+/** Glob patterns for file matching. Single pattern or array of patterns. */
const PatternsSchema = z.union([
z.string().min(1),
z.array(z.string().min(1)).min(1),
]);
+/**
+ * Bundle configuration. Accepts a string pattern, array of patterns, or object.
+ * Patterns prefixed with `!` are exclusions. Patterns prefixed with `+` force
+ * inclusion (bypass .gitignore).
+ */
const BundleConfigSchema = z.union([
- z.string().min(1), // "src/**/*"
- z.array(z.string().min(1)).min(1), // ["src/**/*", "!src/specs"]
+ z.string().min(1),
+ z.array(z.string().min(1)).min(1),
z.object({
+ /** Glob patterns to include in the bundle. */
include: PatternsSchema,
+ /** Custom output file path. Defaults to `/.txt`. */
outfile: z.string().optional(),
- index: z.boolean().default(true), // Include index header in output
+ /** Include file index header in output. Defaults to true. */
+ index: z.boolean().default(true),
+ /** Text to prepend to bundle (e.g., review instructions for LLMs). */
+ prompt: z.string().optional(),
}),
]);
+/**
+ * Upload destination configuration.
+ *
+ * @example
+ * ```ts
+ * upload: {
+ * provider: "gdrive",
+ * clientId: process.env.GDRIVE_CLIENT_ID,
+ * clientSecret: process.env.GDRIVE_CLIENT_SECRET,
+ * folderId: "1abc...",
+ * exclude: ["local", "debug"],
+ * }
+ * ```
+ */
const UploadConfigSchema = z.object({
+ /** Upload provider. Currently only "gdrive" is supported. */
provider: z.literal("gdrive"),
+ /** Google Drive folder ID to upload files to. If omitted, uploads to root. */
folderId: z.string().optional(),
+ /** OAuth 2.0 client ID from Google Cloud Console. */
clientId: z.string().min(1),
+ /** OAuth 2.0 client secret from Google Cloud Console. */
clientSecret: z.string().min(1),
+ /** Bundle names to skip during upload. Supports exact names only. */
+ exclude: z.array(z.string()).optional(),
});
+/** Root configuration for srcpack. */
const ConfigSchema = z.object({
+ /**
+ * Project root directory. Can be absolute or relative to CWD.
+ * @default process.cwd()
+ */
+ root: z.string().default(""),
+ /** Output directory for bundle files. Defaults to ".srcpack". */
outDir: z.string().default(".srcpack"),
+ /** Empty outDir before bundling. Auto-enabled when outDir is inside project root. */
+ emptyOutDir: z.boolean().optional(),
+ /** Upload configuration for cloud storage. Single destination or array. */
upload: z
.union([UploadConfigSchema, z.array(UploadConfigSchema).min(1)])
.optional(),
+ /** Named bundles mapping bundle name to glob patterns or config object. */
bundles: z.record(z.string(), BundleConfigSchema),
});
@@ -69,6 +111,8 @@ export function parseConfig(value: unknown): Config {
}
const config = result.data;
+ // Resolve root: absolute path, relative to CWD, or CWD if empty/unset
+ config.root = config.root ? resolve(expandPath(config.root)) : process.cwd();
config.outDir = expandPath(config.outDir);
for (const bundle of Object.values(config.bundles)) {
diff --git a/tests/fixtures/negation-project/.gitignore b/tests/fixtures/negation-project/.gitignore
new file mode 100644
index 0000000..c509a5d
--- /dev/null
+++ b/tests/fixtures/negation-project/.gitignore
@@ -0,0 +1,2 @@
+build/**
+!build/keep.txt
diff --git a/tests/fixtures/negation-project/build/bundle.js b/tests/fixtures/negation-project/build/bundle.js
new file mode 100644
index 0000000..e05e687
--- /dev/null
+++ b/tests/fixtures/negation-project/build/bundle.js
@@ -0,0 +1 @@
+// This should be ignored
diff --git a/tests/fixtures/negation-project/build/keep.txt b/tests/fixtures/negation-project/build/keep.txt
new file mode 100644
index 0000000..52b8494
--- /dev/null
+++ b/tests/fixtures/negation-project/build/keep.txt
@@ -0,0 +1 @@
+This file should be included despite build/** being ignored
diff --git a/tests/fixtures/negation-project/src/index.ts b/tests/fixtures/negation-project/src/index.ts
new file mode 100644
index 0000000..1e29c45
--- /dev/null
+++ b/tests/fixtures/negation-project/src/index.ts
@@ -0,0 +1 @@
+export const hello = "world";
diff --git a/tests/fixtures/sample-project/prompts/review.md b/tests/fixtures/sample-project/prompts/review.md
new file mode 100644
index 0000000..423826d
--- /dev/null
+++ b/tests/fixtures/sample-project/prompts/review.md
@@ -0,0 +1,4 @@
+Review this code for:
+
+- Security issues
+- Performance problems
diff --git a/tests/manual/login.test.ts b/tests/manual/login.test.ts
index eb67c7e..823be53 100644
--- a/tests/manual/login.test.ts
+++ b/tests/manual/login.test.ts
@@ -42,22 +42,12 @@ async function runCli(
};
}
-describe("real login flow", () => {
- beforeAll(async () => {
- // Validate required env vars
- if (!process.env.GDRIVE_CLIENT_ID) {
- throw new Error(
- "GDRIVE_CLIENT_ID required. Run with:\n" +
- "GDRIVE_CLIENT_ID=xxx GDRIVE_CLIENT_SECRET=xxx bun test:login",
- );
- }
- if (!process.env.GDRIVE_CLIENT_SECRET) {
- throw new Error(
- "GDRIVE_CLIENT_SECRET required. Run with:\n" +
- "GDRIVE_CLIENT_ID=xxx GDRIVE_CLIENT_SECRET=xxx bun test:login",
- );
- }
+const hasCredentials = !!(
+ process.env.GDRIVE_CLIENT_ID && process.env.GDRIVE_CLIENT_SECRET
+);
+describe.skipIf(!hasCredentials)("real login flow", () => {
+ beforeAll(async () => {
await mkdir(TEST_OUTPUT_DIR, { recursive: true });
});
diff --git a/tests/manual/upload.test.ts b/tests/manual/upload.test.ts
index 536ae4d..a0ba489 100644
--- a/tests/manual/upload.test.ts
+++ b/tests/manual/upload.test.ts
@@ -1,31 +1,27 @@
import { existsSync } from "node:fs";
-import { readFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "bun:test";
-import { uploadFile, type UploadResult } from "../../src/gdrive.ts";
+import { uploadFile } from "../../src/gdrive.ts";
import type { UploadConfig } from "../../src/config.ts";
const FIXTURE_DIR = join(import.meta.dir, "fixtures/gdrive-project");
const CREDENTIALS_PATH = join(homedir(), ".config/srcpack/credentials.json");
-function getUploadConfig(): UploadConfig {
- if (!process.env.GDRIVE_CLIENT_ID || !process.env.GDRIVE_CLIENT_SECRET) {
- throw new Error(
- "Required env vars missing. Run with:\n" +
- "bun test:upload (requires .env.local with GDRIVE_CLIENT_ID and GDRIVE_CLIENT_SECRET)",
- );
- }
+const hasCredentials = !!(
+ process.env.GDRIVE_CLIENT_ID && process.env.GDRIVE_CLIENT_SECRET
+);
+function getUploadConfig(): UploadConfig {
return {
provider: "gdrive",
- clientId: process.env.GDRIVE_CLIENT_ID,
- clientSecret: process.env.GDRIVE_CLIENT_SECRET,
+ clientId: process.env.GDRIVE_CLIENT_ID!,
+ clientSecret: process.env.GDRIVE_CLIENT_SECRET!,
folderId: process.env.GDRIVE_FOLDER_ID,
};
}
-describe("gdrive upload", () => {
+describe.skipIf(!hasCredentials)("gdrive upload", () => {
test("should upload a file to Google Drive", async () => {
const config = getUploadConfig();
const filePath = join(FIXTURE_DIR, ".srcpack/main.txt");
diff --git a/tests/unit/bundle.test.ts b/tests/unit/bundle.test.ts
index dee8787..6cad97d 100644
--- a/tests/unit/bundle.test.ts
+++ b/tests/unit/bundle.test.ts
@@ -18,6 +18,7 @@ const forceIncludeDir = join(
import.meta.dir,
"../fixtures/force-include-project",
);
+const negationDir = join(import.meta.dir, "../fixtures/negation-project");
describe("resolvePatterns", () => {
test("should resolve string pattern", async () => {
@@ -156,6 +157,16 @@ describe("resolvePatterns", () => {
expect(files).toContain("docs/private.local.md");
expect(files).not.toContain("docs/guide.md");
});
+
+ test("should respect gitignore negation patterns", async () => {
+ // .gitignore contains: build/** + !build/keep.txt
+ // The negation re-includes build/keep.txt while other build files stay ignored
+ const files = await resolvePatterns("**/*", negationDir);
+
+ expect(files).toContain("src/index.ts");
+ expect(files).toContain("build/keep.txt"); // Re-included by negation
+ expect(files).not.toContain("build/bundle.js"); // Still ignored
+ });
});
describe("formatIndex", () => {
@@ -289,6 +300,73 @@ describe("createBundle", () => {
// Separator is line 1, content starts at line 2
expect(result.index[0]!.startLine).toBe(2);
});
+
+ test("should prepend prompt with separator", async () => {
+ const result = await createBundle(["src/index.ts"], fixturesDir, {
+ prompt: "Review this code for security issues.",
+ });
+
+ expect(result.content).toStartWith("Review this code for security issues.");
+ expect(result.content).toContain("\n\n---\n\n");
+ expect(result.content).toContain("# Index");
+ });
+
+ test("should adjust line numbers for prompt offset", async () => {
+ const result = await createBundle(["src/index.ts"], fixturesDir, {
+ prompt: "Review this code.",
+ });
+
+ // Prompt: 1 line + blank + "---" + blank = 4 lines
+ // Index: header + 1 entry + blank = 3 lines
+ // Separator: 1 line, content starts next line
+ // Content at: 4 + 3 + 1 + 1 = 9
+ expect(result.index[0]!.startLine).toBe(9);
+ });
+
+ test("should handle multi-line prompt", async () => {
+ const result = await createBundle(["src/index.ts"], fixturesDir, {
+ prompt: "Review this code.\nFocus on:\n- Security\n- Performance",
+ });
+
+ expect(result.content).toStartWith("Review this code.");
+ // Prompt: 4 lines + blank + "---" + blank = 7 lines
+ // Index: 3 lines, separator: 1 line, content next line
+ // Content at: 7 + 3 + 1 + 1 = 12
+ expect(result.index[0]!.startLine).toBe(12);
+ });
+
+ test("should prepend prompt without index", async () => {
+ const result = await createBundle(["src/index.ts"], fixturesDir, {
+ prompt: "Review this code.",
+ includeIndex: false,
+ });
+
+ expect(result.content).toStartWith("Review this code.");
+ expect(result.content).toContain("\n\n---\n\n");
+ expect(result.content).not.toContain("# Index");
+ // Prompt: 1 line + blank + "---" + blank = 4 lines
+ // Separator: 1 line, content next line
+ // Content at: 4 + 1 + 1 = 6
+ expect(result.index[0]!.startLine).toBe(6);
+ });
+
+ test("should ignore empty prompt", async () => {
+ const result = await createBundle(["src/index.ts"], fixturesDir, {
+ prompt: "",
+ });
+
+ expect(result.content).toStartWith("# Index");
+ expect(result.content).not.toContain("---");
+ });
+
+ test("should ignore whitespace-only prompt", async () => {
+ const result = await createBundle(["src/index.ts"], fixturesDir, {
+ prompt: " \n \n ",
+ });
+
+ expect(result.content).toStartWith("# Index");
+ expect(result.content).not.toContain("---");
+ });
});
describe("bundleOne", () => {
@@ -334,4 +412,72 @@ describe("bundleOne", () => {
expect(result.content).toContain("# Index");
});
+
+ test("should prepend prompt from config", async () => {
+ const result = await bundleOne(
+ "web",
+ { include: "src/index.ts", prompt: "Review this code." },
+ fixturesDir,
+ );
+
+ expect(result.content).toStartWith("Review this code.");
+ expect(result.content).toContain("\n\n---\n\n");
+ expect(result.content).toContain("# Index");
+ });
+
+ test("should ignore empty prompt in config", async () => {
+ const result = await bundleOne(
+ "web",
+ { include: "src/index.ts", prompt: "" },
+ fixturesDir,
+ );
+
+ expect(result.content).toStartWith("# Index");
+ expect(result.content).not.toContain("---");
+ });
+
+ test("should ignore undefined prompt in config", async () => {
+ const result = await bundleOne(
+ "web",
+ { include: "src/index.ts", prompt: undefined },
+ fixturesDir,
+ );
+
+ expect(result.content).toStartWith("# Index");
+ expect(result.content).not.toContain("---");
+ });
+
+ test("should load prompt from file when path starts with ./", async () => {
+ const result = await bundleOne(
+ "web",
+ { include: "src/index.ts", prompt: "./prompts/review.md" },
+ fixturesDir,
+ );
+
+ expect(result.content).toStartWith("Review this code for:");
+ expect(result.content).toContain("- Security issues");
+ expect(result.content).toContain("\n\n---\n\n");
+ });
+
+ test("should attempt to load prompt from ~/ path", async () => {
+ // Verify ~/ paths are treated as file paths (throws for non-existent file)
+ await expect(
+ bundleOne(
+ "web",
+ { include: "src/index.ts", prompt: "~/non-existent-srcpack-test.md" },
+ fixturesDir,
+ ),
+ ).rejects.toThrow("ENOENT");
+ });
+
+ test("should use literal prompt when not a path", async () => {
+ const result = await bundleOne(
+ "web",
+ { include: "src/index.ts", prompt: "Check for bugs." },
+ fixturesDir,
+ );
+
+ expect(result.content).toStartWith("Check for bugs.");
+ expect(result.content).not.toContain("./");
+ });
});
diff --git a/tests/unit/config.test.ts b/tests/unit/config.test.ts
index 5a2ebcb..0541a7b 100644
--- a/tests/unit/config.test.ts
+++ b/tests/unit/config.test.ts
@@ -106,6 +106,41 @@ describe("parseConfig", () => {
});
describe("default values", () => {
+ test("should default root to process.cwd()", () => {
+ const config = parseConfig({
+ bundles: { web: "src/**/*" },
+ });
+
+ expect(config.root).toBe(process.cwd());
+ });
+
+ test("should resolve relative root to absolute path", () => {
+ const config = parseConfig({
+ root: "./subdir",
+ bundles: { web: "src/**/*" },
+ });
+
+ expect(config.root).toBe(join(process.cwd(), "subdir"));
+ });
+
+ test("should resolve tilde root to home directory", () => {
+ const config = parseConfig({
+ root: "~/projects",
+ bundles: { web: "src/**/*" },
+ });
+
+ expect(config.root).toBe(join(homedir(), "projects"));
+ });
+
+ test("should keep absolute root path unchanged", () => {
+ const config = parseConfig({
+ root: "/absolute/path",
+ bundles: { web: "src/**/*" },
+ });
+
+ expect(config.root).toBe("/absolute/path");
+ });
+
test("should default outDir to .srcpack", () => {
const config = parseConfig({
bundles: { web: "src/**/*" },
@@ -122,6 +157,32 @@ describe("parseConfig", () => {
expect(config.upload).toBeUndefined();
});
+ test("should leave emptyOutDir undefined when not provided", () => {
+ const config = parseConfig({
+ bundles: { web: "src/**/*" },
+ });
+
+ expect(config.emptyOutDir).toBeUndefined();
+ });
+
+ test("should accept emptyOutDir true", () => {
+ const config = parseConfig({
+ bundles: { web: "src/**/*" },
+ emptyOutDir: true,
+ });
+
+ expect(config.emptyOutDir).toBe(true);
+ });
+
+ test("should accept emptyOutDir false", () => {
+ const config = parseConfig({
+ bundles: { web: "src/**/*" },
+ emptyOutDir: false,
+ });
+
+ expect(config.emptyOutDir).toBe(false);
+ });
+
test("should accept single upload config", () => {
const config = parseConfig({
bundles: { web: "src/**/*" },
@@ -163,6 +224,36 @@ describe("parseConfig", () => {
expect(Array.isArray(config.upload)).toBe(true);
expect(config.upload).toHaveLength(2);
});
+
+ test("should accept upload.exclude as array of bundle names", () => {
+ const config = parseConfig({
+ bundles: { web: "src/**/*", local: "local/**/*" },
+ upload: {
+ provider: "gdrive",
+ clientId: "id",
+ clientSecret: "secret",
+ exclude: ["local", "debug"],
+ },
+ });
+
+ expect((config.upload as UploadConfig).exclude).toEqual([
+ "local",
+ "debug",
+ ]);
+ });
+
+ test("should leave upload.exclude undefined when not provided", () => {
+ const config = parseConfig({
+ bundles: { web: "src/**/*" },
+ upload: {
+ provider: "gdrive",
+ clientId: "id",
+ clientSecret: "secret",
+ },
+ });
+
+ expect((config.upload as UploadConfig).exclude).toBeUndefined();
+ });
});
describe("path expansion", () => {
@@ -281,6 +372,11 @@ describe("type inference", () => {
}>().toMatchTypeOf();
});
+ test("Config has required root after parsing", () => {
+ expectTypeOf().toHaveProperty("root");
+ expectTypeOf().toEqualTypeOf();
+ });
+
test("Config has required outDir after parsing", () => {
expectTypeOf().toHaveProperty("outDir");
expectTypeOf().toEqualTypeOf();
@@ -300,6 +396,18 @@ describe("type inference", () => {
>();
});
+ test("Config has optional emptyOutDir property", () => {
+ expectTypeOf().toHaveProperty("emptyOutDir");
+ expectTypeOf().toEqualTypeOf();
+ });
+
+ test("UploadConfig has optional exclude property", () => {
+ expectTypeOf().toHaveProperty("exclude");
+ expectTypeOf().toEqualTypeOf<
+ string[] | undefined
+ >();
+ });
+
test("BundleConfig accepts string pattern", () => {
expectTypeOf<"src/**/*">().toMatchTypeOf();
});