From d1bc84c519fd3a532bb0fe3f69a98d7ca802b240 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 15:39:52 +0300 Subject: [PATCH 01/17] =?UTF-8?q?chore:=20Phase=200=20=E2=80=94=20repo=20i?= =?UTF-8?q?nit=20(local=20deps,=20AGENTS.md=20with=20full=20plan)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .mise/tasks/lint:fix | 3 - AGENTS.md | 83 +- package-lock.json | 3353 ++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- 4 files changed, 3428 insertions(+), 15 deletions(-) delete mode 100755 .mise/tasks/lint:fix create mode 100644 package-lock.json diff --git a/.mise/tasks/lint:fix b/.mise/tasks/lint:fix deleted file mode 100755 index bcdc53b..0000000 --- a/.mise/tasks/lint:fix +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Run Biome lint with auto-fix" -biome lint --write . \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index b4dfe32..fd62585 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,15 +1,13 @@ # AGENTS.md -## Build & Test Commands +## Build & Test Commands (Windows / Node.js) -- **Build**: `mise run build` or `bun build ./src/index.ts --outdir dist --target bun` -- **Test**: `mise run test` or `bun test` -- **Single Test**: `bun test BackgroundTask.test.ts` (use file glob pattern) -- **Watch Mode**: `bun test --watch` -- **Lint**: `mise run lint` or `bun run lint` (biome) -- **Fix Lint**: `mise run lint:fix` or `bun run lint:fix` (biome --write) -- **Format**: `mise run format` or `bun run format` (biome format --write) -- **Check (Lint + Format)**: `bun run check` (biome check --write) +- **Build**: `npx tsc -p tsconfig.build.json && copyfiles -u 1 "src/command/**/*" dist/` +- **Test**: `npx vitest run` +- **Single Test**: `npx vitest run --reporter=verbose src/sync/paths.test.ts` +- **Lint**: `npx biome lint .` +- **Check**: `npx biome check --write .` +- **Deploy**: copy `dist/` to `C:\Users\Aleks\.config\opencode\plugins\opencode-synced\` ## PR & Commit Guidelines @@ -70,3 +68,70 @@ - **Type**: ES Module package for opencode plugin system - **Target**: Bun runtime, ES2021+ - **Purpose**: Sync global opencode config across machines via GitHub, with optional secrets support (e.g., 1Password backend) + +## OpenCode Desktop Context (Windows) + +- OpenCode v1.15.7 from anomalyco/opencode (Electron/Node.js, not Bun Terminal) +- Desktop config: `C:\Users\Aleks\.config\opencode\` (XDG-style, NOT `%APPDATA%`) +- Plugin API: local at `C:\Users\Aleks\.config\opencode\node_modules\@opencode-ai\plugin\` v1.4.7 +- `ctx.$` is `undefined` on Desktop (Bun shell not available), but `createNodeShell()` fallback exists +- Plugin config: `C:\Users\Aleks\.config\opencode\opencode-synced.jsonc` tells which GitHub repo to sync +- Session DB: `C:\Users\Aleks\.local\share\opencode\opencode.db` (SQLite) +- Desktop projects store: `%APPDATA%\ai.opencode.desktop\opencode.global.dat` + +## FORK PLAN — 6 Phases + +### Phase 0 ✅ DONE +- [x] Clone latest `main` from `github.com/iHildy/opencode-synced` (commit a627673) +- [x] Fix `package.json`: `@opencode-ai/plugin` → local `file:` reference in devDependencies +- [x] `npm install`, `npx tsc -p tsconfig.build.json` — build passes +- [ ] ~~`bun test`~~ → use `npx vitest run` (no bun on Windows) + +### Phase 1: path.ts — XDG on all platforms (remove win32 APPDATA) +**Files**: `src/sync/paths.ts`, `src/sync/paths.test.ts` +1. Remove `if (platform === 'win32')` block with `env.APPDATA`/`env.LOCALAPPDATA` +2. `dataDir` = `env.XDG_DATA_HOME ?? path.join(home, '.local', 'share')` +3. `configDir` = `env.XDG_CONFIG_HOME ?? path.join(home, '.config')` +4. Fix `env.opencode_config_dir` → `env.OPENCODE_CONFIG_DIR` +5. Update tests for Windows expectations (no more `%APPDATA%`) + +### Phase 2: Notifications — toasts + logs on key lifecycle points +**Files**: `src/sync/utils.ts`, `src/sync/service.ts` +1. Add `notify(client, { emoji, title, message, variant })` — unified entry point +2. `runStartup()` → `"🔄 Sync starting…"`, `"✅ opencode-synced ready"` +3. `pull()` → `"📥 Pulling…"`, `"📥 Pull complete — X changes"` +4. `push()` → `"📤 Pushing…"`, `"📤 Push successful"` +5. `link()` → `"🔗 Linking…"`, `"🔗 Linked to {repo}"` +6. `syncSessions()` → `"💾 Syncing sessions…"`, result summary +7. Errors → `"❌ {message}"` with variant='error' + +### Phase 3: Session sync — union-merge per record +**New file**: `src/sync/session-merge.ts` +**Edit**: `src/sync/session-db.ts`, `src/sync/service.ts` + +**Problem**: Current `exportSessions` / `importSessions` use `skip-if-exists` — updated sessions are never re-synced. +**Solution**: Replace with `syncSessions(localDB, remoteDir)`: +1. Read ALL sessions from local SQLite → `Map` +2. Read ALL session .json files from repo → `Map` +3. For each unique id: `merge(local, remote)` per record: + - session meta: newer `time_updated` wins + - messages: union by `message.id`, each picks max `time_updated` + - parts: union by `part.id` nested in messages + - todos, session_messages, shares: union by id +4. Write merged to both sides (SQLite + .json) +5. Called both on pull (after fetchAndFastForward) and push (before syncLocalToRepo) + +### Phase 4: includeProjects — sync opencode.global.dat +**Files**: `src/sync/config.ts`, `src/sync/service.ts` +1. Add `includeProjects: boolean = false` config field +2. Read `opencode.global.dat` from Desktop AppData +3. Copy to `data/opencode.global.dat` in repo on push +4. Copy from repo on pull +5. Warn: Desktop restart required to apply projects + +### Phase 5: Build, deploy, test +1. `npx tsc -p tsconfig.build.json && copyfiles -u 1 "src/command/**/*" dist/` +2. Copy `dist/` → `C:\Users\Aleks\.config\opencode\plugins\opencode-synced\` +3. Restart OpenCode Desktop +4. Test `/sync-pull`, `/sync-push`, `/sync-status` +5. Test session creation + push/pull between machines diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1389e1d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3353 @@ +{ + "name": "opencode-synced", + "version": "0.9.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "opencode-synced", + "version": "0.9.0", + "devDependencies": { + "@biomejs/biome": "2.3.10", + "@commitlint/cli": "^20.2.0", + "@commitlint/config-conventional": "^20.2.0", + "@opencode-ai/plugin": "file:C:\\Users\\Aleks\\.config\\opencode\\node_modules\\@opencode-ai\\plugin", + "@types/node": "^20.11.5", + "bun-types": "latest", + "husky": "^9.1.7", + "lint-staged": "^16.2.7", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + } + }, + "C:/Users/Aleks/.config/opencode/node_modules/@opencode-ai/plugin": { + "version": "1.4.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@opencode-ai/sdk": "1.4.7", + "effect": "4.0.0-beta.48", + "zod": "4.1.8" + }, + "devDependencies": { + "@opentui/core": "0.1.99", + "@opentui/solid": "0.1.99", + "@tsconfig/node22": "22.0.2", + "@types/node": "22.13.9", + "@typescript/native-preview": "7.0.0-dev.20251207.1", + "typescript": "5.8.2" + }, + "peerDependencies": { + "@opentui/core": ">=0.1.99", + "@opentui/solid": ">=0.1.99" + }, + "peerDependenciesMeta": { + "@opentui/core": { + "optional": true + }, + "@opentui/solid": { + "optional": true + } + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.10.tgz", + "integrity": "sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.3.10", + "@biomejs/cli-darwin-x64": "2.3.10", + "@biomejs/cli-linux-arm64": "2.3.10", + "@biomejs/cli-linux-arm64-musl": "2.3.10", + "@biomejs/cli-linux-x64": "2.3.10", + "@biomejs/cli-linux-x64-musl": "2.3.10", + "@biomejs/cli-win32-arm64": "2.3.10", + "@biomejs/cli-win32-x64": "2.3.10" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.10.tgz", + "integrity": "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.10.tgz", + "integrity": "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.10.tgz", + "integrity": "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.10.tgz", + "integrity": "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.10.tgz", + "integrity": "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.10.tgz", + "integrity": "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.10.tgz", + "integrity": "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.10.tgz", + "integrity": "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@commitlint/cli": { + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.3.tgz", + "integrity": "sha512-OJdL0EXWD5y9LPa0nr/geOwzaS8BsdaybKkcloB0JgsguGxNv2R+hC2FTPqrAcprg35zF33KOQerY0x8W1aesA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^20.5.0", + "@commitlint/lint": "^20.5.3", + "@commitlint/load": "^20.5.3", + "@commitlint/read": "^20.5.0", + "@commitlint/types": "^20.5.0", + "tinyexec": "^1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.3.tgz", + "integrity": "sha512-j34Qqeaa152chJgz2ysyk0BCpHenJn1lV0Rx0VXf8k3ccQcED+48EZrzMvo9jLmJUyBrrBwvu89I+2er4gW7QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.5.0", + "conventional-changelog-conventionalcommits": "^9.2.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.5.0.tgz", + "integrity": "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.5.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.3.tgz", + "integrity": "sha512-4i4AgNvH62owG9MwSiWKrle7HGNpBHHdLnWFIp5fTsHUYe5kRuh15t08L/0pdbbrRk8JKXQxxN4hZQcn+szkrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.5.0", + "es-toolkit": "^1.46.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", + "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.5.0.tgz", + "integrity": "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.5.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.5.0.tgz", + "integrity": "sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.5.0", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.3.tgz", + "integrity": "sha512-M7JbWBNr2gXKaPc4i/KipsuW1gkDHpj35KPjWtKy3Z+2AQw5wu1gBi1LIO0uoaij67CqY4K8PxPZSGens4evCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^20.5.0", + "@commitlint/parse": "^20.5.0", + "@commitlint/rules": "^20.5.3", + "@commitlint/types": "^20.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.3.tgz", + "integrity": "sha512-1FDZWuKyu98Myb8i7Tp31jPU2rZpOwAdYRyJcy2KoGg7Xk2A+bgHN8smhMaaNSNkmE8fwt53BokywZq8Gv/5XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.5.0", + "@commitlint/execute-rule": "^20.0.0", + "@commitlint/resolve-extends": "^20.5.3", + "@commitlint/types": "^20.5.0", + "cosmiconfig": "^9.0.1", + "cosmiconfig-typescript-loader": "^6.1.0", + "es-toolkit": "^1.46.0", + "is-plain-obj": "^4.1.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/message": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz", + "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.5.0.tgz", + "integrity": "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^20.5.0", + "conventional-changelog-angular": "^8.2.0", + "conventional-commits-parser": "^6.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.5.0.tgz", + "integrity": "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^20.4.3", + "@commitlint/types": "^20.5.0", + "git-raw-commits": "^5.0.0", + "minimist": "^1.2.8", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.3.tgz", + "integrity": "sha512-+ogW9v/u9JqpvAgTrLra/YTFo0KkjU6iNblF89pPsj4NebNc+DAWctsludwezI8YnsjBmfHpApSwcXprN/f/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^20.5.0", + "@commitlint/types": "^20.5.0", + "es-toolkit": "^1.46.0", + "global-directory": "^5.0.0", + "import-meta-resolve": "^4.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.3.tgz", + "integrity": "sha512-MPlMnb9D3wbszYMp+1hPtuhtPJndRo6I6yfkZVA4+jR8w7Kqp0u2u/Y+gzbaItx5Lltq5rw7FSZQWJMoXUC4NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^20.5.3", + "@commitlint/message": "^20.4.3", + "@commitlint/to-lines": "^20.0.0", + "@commitlint/types": "^20.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", + "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "20.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz", + "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.5.0.tgz", + "integrity": "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^6.3.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@conventional-changelog/git-client": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.7.0.tgz", + "integrity": "sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/child-process-utils": "^1.0.0", + "@simple-libs/stream-utils": "^1.2.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.4.0" + }, + "peerDependenciesMeta": { + "conventional-commits-filter": { + "optional": true + }, + "conventional-commits-parser": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@opencode-ai/plugin": { + "resolved": "C:/Users/Aleks/.config/opencode/node_modules/@opencode-ai/plugin", + "link": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@simple-libs/child-process-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", + "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@simple-libs/stream-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", + "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "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": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "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/bun-types": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.14.tgz", + "integrity": "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.1.tgz", + "integrity": "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.1.tgz", + "integrity": "sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", + "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", + "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "2.6.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-toolkit": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "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/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/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz", + "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@conventional-changelog/git-client": "^2.6.0", + "meow": "^13.0.0" + }, + "bin": { + "git-raw-commits": "src/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/global-directory": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", + "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "6.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "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/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lint-staged": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", + "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.3", + "listr2": "^9.0.5", + "picomatch": "^4.0.3", + "string-argv": "^0.3.2", + "tinyexec": "^1.0.4", + "yaml": "^2.8.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "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/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "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.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "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": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/package.json b/package.json index 6b5f16c..fa2cfdc 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,8 @@ "access": "public" }, "files": ["dist"], - "dependencies": { - "@opencode-ai/plugin": "1.0.85" - }, "devDependencies": { + "@opencode-ai/plugin": "file:C:\\Users\\Aleks\\.config\\opencode\\node_modules\\@opencode-ai\\plugin", "@biomejs/biome": "2.3.10", "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", From fbcb0463510f8c7041cc0debc7c4078bdf3c8957 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 15:45:48 +0300 Subject: [PATCH 02/17] fix: rename .mise/tasks/lint:fix to lint-fix for Windows compatibility --- .mise/tasks/lint-fix | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .mise/tasks/lint-fix diff --git a/.mise/tasks/lint-fix b/.mise/tasks/lint-fix new file mode 100644 index 0000000..4768a4a --- /dev/null +++ b/.mise/tasks/lint-fix @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +#MISE description="Run Biome lint with auto-fix" +biome lint --write . From b154765f0362129a31d90390522592955566eac6 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 15:50:07 +0300 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20Phase=201=20=E2=80=94=20XDG=20pat?= =?UTF-8?q?hs=20on=20all=20platforms,=20remove=20win32=20APPDATA=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sync/paths.test.ts | 10 ++++------ src/sync/paths.ts | 10 +--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/sync/paths.test.ts b/src/sync/paths.test.ts index 57b60d8..25e9621 100644 --- a/src/sync/paths.test.ts +++ b/src/sync/paths.test.ts @@ -16,21 +16,19 @@ describe('resolveXdgPaths', () => { it('resolves windows defaults', () => { const env = { USERPROFILE: 'C:\\Users\\Test', - APPDATA: 'C:\\Users\\Test\\AppData\\Roaming', - LOCALAPPDATA: 'C:\\Users\\Test\\AppData\\Local', } as NodeJS.ProcessEnv; const paths = resolveXdgPaths(env, 'win32'); - expect(paths.configDir).toBe('C:\\Users\\Test\\AppData\\Roaming'); - expect(paths.dataDir).toBe('C:\\Users\\Test\\AppData\\Local'); + expect(paths.configDir).toBe('C:\\Users\\Test\\.config'); + expect(paths.dataDir).toBe('C:\\Users\\Test\\.local\\share'); }); }); describe('resolveSyncLocations', () => { - it('respects opencode_config_dir', () => { + it('respects OPENCODE_CONFIG_DIR', () => { const env = { HOME: '/home/test', - opencode_config_dir: '/custom/opencode', + OPENCODE_CONFIG_DIR: '/custom/opencode', } as NodeJS.ProcessEnv; const locations = resolveSyncLocations(env, 'linux'); diff --git a/src/sync/paths.ts b/src/sync/paths.ts index 54bd147..0ad541b 100644 --- a/src/sync/paths.ts +++ b/src/sync/paths.ts @@ -86,14 +86,6 @@ export function resolveXdgPaths( }; } - if (platform === 'win32') { - const configDir = env.APPDATA ?? path.join(homeDir, 'AppData', 'Roaming'); - const dataDir = env.LOCALAPPDATA ?? path.join(homeDir, 'AppData', 'Local'); - // Windows doesn't have XDG_STATE_HOME equivalent, use LOCALAPPDATA - const stateDir = env.LOCALAPPDATA ?? path.join(homeDir, 'AppData', 'Local'); - return { homeDir, configDir, dataDir, stateDir }; - } - const configDir = env.XDG_CONFIG_HOME ?? path.join(homeDir, '.config'); const dataDir = env.XDG_DATA_HOME ?? path.join(homeDir, '.local', 'share'); const stateDir = env.XDG_STATE_HOME ?? path.join(homeDir, '.local', 'state'); @@ -106,7 +98,7 @@ export function resolveSyncLocations( platform: NodeJS.Platform = process.platform ): SyncLocations { const xdg = resolveXdgPaths(env, platform); - const customConfigDir = env.opencode_config_dir; + const customConfigDir = env.OPENCODE_CONFIG_DIR; const configRoot = customConfigDir ? path.resolve(expandHome(customConfigDir, xdg.homeDir)) : path.join(xdg.configDir, 'opencode'); From c8ab495b6fc463434ef403df9b1240040f15661c Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 15:52:03 +0300 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20Phase=202=20=E2=80=94=20notificat?= =?UTF-8?q?ions=20for=20sync=20lifecycle=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add notify() helper: emoji + toast + log in one call - startupSync: 🔄 Syncing..., ✅ ready, ❌ error - pull: 📥 Pulling..., 📥 result - push: 📤 Pushing..., 📤 result - link: 🔗 Linking..., 🔗 result - init: 🚀 Sync configured --- src/sync/service.ts | 28 ++++++++++++++++++++++------ src/sync/utils.ts | 14 ++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/sync/service.ts b/src/sync/service.ts index 3604254..57fcc38 100644 --- a/src/sync/service.ts +++ b/src/sync/service.ts @@ -51,6 +51,7 @@ import { import { createLogger, extractTextFromResponse, + notify, resolveSmallModel, showToast, unwrapData, @@ -600,17 +601,19 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } catch (error) { const message = `Failed to load opencode-synced config: ${formatError(error)}`; log.error(message, { path: locations.syncConfigPath }); - await showToast( + await notify( ctx.client, - `Failed to load opencode-synced config. Check ${locations.syncConfigPath} for JSON errors.`, + '❌', + `Failed to load config. Check ${locations.syncConfigPath} for JSON errors.`, 'error' ); return; } if (!config) { stopTursoSyncLoop(); - await showToast( + await notify( ctx.client, + 'ℹ️', 'Configure opencode-synced with /sync-init or link to an existing repo with /sync-link', 'info' ); @@ -618,6 +621,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } try { assertValidSecretsBackend(config); + await notify(ctx.client, '🔄', 'Syncing config...', 'info'); let tursoWarning: string | null = null; if (isTursoSessionBackend(config)) { try { @@ -632,13 +636,14 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { runSecretsPullIfConfigured, }); ensureTursoSyncLoop(config); + await notify(ctx.client, '✅', 'opencode-synced ready', 'success'); if (tursoWarning) { log.warn(tursoWarning); await showToast(ctx.client, tursoWarning, 'warning'); } } catch (error) { log.error('Startup sync failed', { error: formatError(error) }); - await showToast(ctx.client, formatError(error), 'error'); + await notify(ctx.client, '❌', formatError(error), 'error'); } }), status: async () => { @@ -796,6 +801,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } ensureTursoSyncLoop(config); + await notify(ctx.client, '🚀', `Sync configured — ${repoIdentifier} (${resolveRepoBranch(config)})`, 'success'); return lines.join('\n'); }), link: (options: LinkOptions) => @@ -811,6 +817,8 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { ); } + await notify(ctx.client, '🔗', 'Linking to sync repo...', 'info'); + const found = await findSyncRepo(ctx.$, options.repo, { disableAutoDiscovery: disableAutoRepoDiscovery, }); @@ -904,7 +912,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { lines.push('', ...linkNotes); } - await showToast(ctx.client, 'Config synced. Restart opencode to apply.', 'info'); + await notify(ctx.client, '🔗', `Linked to ${found.owner}/${found.name}. Restart opencode to apply.`, 'success'); return lines.join('\n'); }), pull: () => @@ -924,10 +932,13 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { ); } + await notify(ctx.client, '📥', 'Pulling config from GitHub...', 'info'); + const update = await fetchAndFastForward(ctx.$, repoRoot, branch); if (!update.updated) { const tursoSummary = await runForegroundTursoCycle(config, 'pull-up-to-date'); ensureTursoSyncLoop(config); + await notify(ctx.client, '📥', 'Already up to date', 'success'); if (tursoSummary) { return ['Already up to date.', tursoSummary].join('\n'); } @@ -947,7 +958,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { const tursoSummary = await runForegroundTursoCycle(config, 'pull-updated'); ensureTursoSyncLoop(config); - await showToast(ctx.client, 'Config updated. Restart opencode to apply.', 'info'); + await notify(ctx.client, '📥', 'Config updated. Restart opencode to apply.', 'success'); if (tursoSummary) { return [ 'Remote config applied. Restart opencode to use new settings.', @@ -972,6 +983,8 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { ); } + await notify(ctx.client, '📤', 'Pushing config to GitHub...', 'info'); + const overrides = await loadOverrides(locations); const plan = buildSyncPlan(config, locations, repoRoot); await syncLocalToRepo(plan, overrides, { @@ -985,6 +998,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { ensureTursoSyncLoop(config); try { const secretsResult = await runSecretsPushIfConfigured(config); + await notify(ctx.client, '📤', 'No local changes to push', 'info'); const lines: string[] = []; if (secretsResult === 'pushed') { lines.push('No local changes to push. Secrets updated.'); @@ -1026,6 +1040,8 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { const tursoSummary = await runForegroundTursoCycle(config, 'push-updated'); ensureTursoSyncLoop(config); + await notify(ctx.client, '📤', `Pushed: ${message}`, 'success'); + if (secretsFailure) { const lines = [`Pushed changes: ${message}. Secrets push failed: ${secretsFailure}`]; if (tursoSummary) { diff --git a/src/sync/utils.ts b/src/sync/utils.ts index 5b1af50..8655fcd 100644 --- a/src/sync/utils.ts +++ b/src/sync/utils.ts @@ -52,6 +52,20 @@ export async function showToast( } } +type NotifyVariant = 'info' | 'success' | 'warning' | 'error'; + +export async function notify( + client: Client, + emoji: string, + message: string, + variant: NotifyVariant = 'info' +): Promise { + const full = `${emoji} ${message}`; + const level = variant === 'error' ? 'error' : variant === 'warning' ? 'warn' : 'info'; + log(client, level, full); + await showToast(client, full, variant); +} + export function unwrapData(response: unknown): T | null { if (!response || typeof response !== 'object') return null; const maybeError = (response as { error?: unknown }).error; From d49f32416f8b1bcffcd1694dc34f7b84898ecbd3 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 15:57:46 +0300 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20Phase=203=20=E2=80=94=20session?= =?UTF-8?q?=20union-merge=20per=20record?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - session-db.ts: read/write sessions from SQLite and JSON dir - session-merge.ts: syncSessions() — union-merge per record (messages, parts, todos, session_messages, shares by id+time_updated) - service.ts: integrate syncSessions in pull, push, runStartup - @types/node: ^22.15.0 for node:sqlite types --- package-lock.json | 8 +- package.json | 12 +- src/sync/service.ts | 32 +++++ src/sync/session-db.ts | 273 ++++++++++++++++++++++++++++++++++++++ src/sync/session-merge.ts | 123 +++++++++++++++++ 5 files changed, 440 insertions(+), 8 deletions(-) create mode 100644 src/sync/session-db.ts create mode 100644 src/sync/session-merge.ts diff --git a/package-lock.json b/package-lock.json index 1389e1d..5673eb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", "@opencode-ai/plugin": "file:C:\\Users\\Aleks\\.config\\opencode\\node_modules\\@opencode-ai\\plugin", - "@types/node": "^20.11.5", + "@types/node": "^22.19.19", "bun-types": "latest", "husky": "^9.1.7", "lint-staged": "^16.2.7", @@ -1426,9 +1426,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "version": "22.19.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", + "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fa2cfdc..3ff5b34 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,15 @@ "publishConfig": { "access": "public" }, - "files": ["dist"], + "files": [ + "dist" + ], "devDependencies": { - "@opencode-ai/plugin": "file:C:\\Users\\Aleks\\.config\\opencode\\node_modules\\@opencode-ai\\plugin", "@biomejs/biome": "2.3.10", "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", - "@types/node": "^20.11.5", + "@opencode-ai/plugin": "file:C:\\Users\\Aleks\\.config\\opencode\\node_modules\\@opencode-ai\\plugin", + "@types/node": "^22.19.19", "bun-types": "latest", "husky": "^9.1.7", "lint-staged": "^16.2.7", @@ -47,6 +49,8 @@ "prepare": "husky" }, "lint-staged": { - "*.{js,ts,json}": ["biome check --write --no-errors-on-unmatched"] + "*.{js,ts,json}": [ + "biome check --write --no-errors-on-unmatched" + ] } } diff --git a/src/sync/service.ts b/src/sync/service.ts index 57fcc38..65c81b1 100644 --- a/src/sync/service.ts +++ b/src/sync/service.ts @@ -48,6 +48,7 @@ import { isRetryableTursoError, type TursoSyncPreference, } from './turso.js'; +import { syncSessions } from './session-merge.js'; import { createLogger, extractTextFromResponse, @@ -950,6 +951,13 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { await syncRepoToLocal(plan, overrides); await runSecretsPullIfConfigured(config); + if (config.includeSessions && !isTursoSessionBackend(config)) { + const sessionDbPath = path.join(locations.xdg.dataDir, 'opencode', 'opencode.db'); + const sessionsDir = path.join(repoRoot, 'data', 'sessions'); + const result = await syncSessions(sessionDbPath, sessionsDir); + log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); + } + await updateState(locations, { lastPull: new Date().toISOString(), lastRemoteUpdate: new Date().toISOString(), @@ -987,6 +995,14 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { const overrides = await loadOverrides(locations); const plan = buildSyncPlan(config, locations, repoRoot); + + if (config.includeSessions && !isTursoSessionBackend(config)) { + const sessionDbPath = path.join(locations.xdg.dataDir, 'opencode', 'opencode.db'); + const sessionsDir = path.join(repoRoot, 'data', 'sessions'); + const result = await syncSessions(sessionDbPath, sessionsDir); + log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); + } + await syncLocalToRepo(plan, overrides, { overridesPath: locations.overridesPath, allowMcpSecrets: canCommitMcpSecrets(config), @@ -1389,6 +1405,14 @@ async function runStartup( const plan = buildSyncPlan(config, locations, repoRoot); await syncRepoToLocal(plan, overrides); await options.runSecretsPullIfConfigured(config); + + if (config.includeSessions && !isTursoSessionBackend(config)) { + const sessionDbPath = path.join(locations.xdg.dataDir, 'opencode', 'opencode.db'); + const sessionsDir = path.join(repoRoot, 'data', 'sessions'); + const result = await syncSessions(sessionDbPath, sessionsDir); + log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); + } + await updateState(locations, { lastPull: new Date().toISOString(), lastRemoteUpdate: new Date().toISOString(), @@ -1399,6 +1423,14 @@ async function runStartup( const overrides = await loadOverrides(locations); const plan = buildSyncPlan(config, locations, repoRoot); + + if (config.includeSessions && !isTursoSessionBackend(config)) { + const sessionDbPath = path.join(locations.xdg.dataDir, 'opencode', 'opencode.db'); + const sessionsDir = path.join(repoRoot, 'data', 'sessions'); + const result = await syncSessions(sessionDbPath, sessionsDir); + log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); + } + await syncLocalToRepo(plan, overrides, { overridesPath: locations.overridesPath, allowMcpSecrets: canCommitMcpSecrets(config), diff --git a/src/sync/session-db.ts b/src/sync/session-db.ts new file mode 100644 index 0000000..4c3a809 --- /dev/null +++ b/src/sync/session-db.ts @@ -0,0 +1,273 @@ +import { DatabaseSync } from 'node:sqlite'; +import fs from 'node:fs'; +import path from 'node:path'; + +export interface SessionMeta { + id: string; + project_id: string | null; + parent_id: string | null; + slug: string | null; + directory: string | null; + title: string | null; + version: number | null; + share_url: string | null; + summary_additions: number | null; + summary_deletions: number | null; + summary_files: number | null; + summary_diffs: string | null; + revert: string | null; + permission: string | null; + time_created: string | null; + time_updated: string | null; + time_compacting: string | null; + time_archived: string | null; + workspace_id: string | null; + path: string | null; + agent: string | null; + model: string | null; + cost: number | null; + tokens_input: number | null; + tokens_output: number | null; + tokens_reasoning: number | null; + tokens_cache_read: number | null; + tokens_cache_write: number | null; +} + +export interface Message { + id: string; + session_id: string; + time_created: string | null; + time_updated: string | null; + data: string | null; + parts: Part[]; +} + +export interface Part { + id: string; + message_id: string; + session_id: string; + time_created: string | null; + time_updated: string | null; + data: string | null; +} + +export interface SessionMessage { + id: string; + session_id: string; + type: string | null; + time_created: string | null; + time_updated: string | null; + data: string | null; +} + +export interface Todo { + session_id: string; + content: string | null; + status: string | null; + priority: number | null; + position: number | null; + time_created: string | null; + time_updated: string | null; +} + +export interface SessionShare { + session_id: string; + id: string; + secret: string | null; + url: string | null; + time_created: string | null; + time_updated: string | null; +} + +export interface Session { + session: SessionMeta; + messages: Message[]; + session_messages: SessionMessage[]; + todos: Todo[]; + session_shares: SessionShare[]; +} + +const SESSION_COLUMNS = [ + 'id', 'project_id', 'parent_id', 'slug', 'directory', + 'title', 'version', 'share_url', 'summary_additions', + 'summary_deletions', 'summary_files', 'summary_diffs', + 'revert', 'permission', 'time_created', 'time_updated', + 'time_compacting', 'time_archived', 'workspace_id', + 'path', 'agent', 'model', 'cost', 'tokens_input', + 'tokens_output', 'tokens_reasoning', 'tokens_cache_read', + 'tokens_cache_write', +]; + +const MESSAGE_COLUMNS = ['id', 'session_id', 'time_created', 'time_updated', 'data']; +const PART_COLUMNS = ['id', 'message_id', 'session_id', 'time_created', 'time_updated', 'data']; +const SESSION_MESSAGE_COLUMNS = ['id', 'session_id', 'type', 'time_created', 'time_updated', 'data']; +const TODO_COLUMNS = ['session_id', 'content', 'status', 'priority', 'position', 'time_created', 'time_updated']; +const SHARE_COLUMNS = ['session_id', 'id', 'secret', 'url', 'time_created', 'time_updated']; + +function openDB(dbPath: string): DatabaseSync { + return new DatabaseSync(dbPath); +} + +function readSessionMeta(db: DatabaseSync, id: string): SessionMeta | null { + const row = db.prepare(`SELECT ${SESSION_COLUMNS.join(', ')} FROM session WHERE id = ?`).get(id) as Record | undefined; + if (!row) return null; + return row as unknown as SessionMeta; +} + +function readMessages(db: DatabaseSync, sessionId: string): Message[] { + const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS.join(', ')} FROM message WHERE session_id = ?`).all(sessionId) as Record[]; + return rows.map((row) => { + const msg = row as unknown as Message; + const partRows = db.prepare(`SELECT ${PART_COLUMNS.join(', ')} FROM part WHERE message_id = ?`).all(msg.id) as Record[]; + msg.parts = partRows.map((pr) => pr as unknown as Part); + return msg; + }); +} + +function readSessionMessages(db: DatabaseSync, sessionId: string): SessionMessage[] { + const rows = db.prepare(`SELECT ${SESSION_MESSAGE_COLUMNS.join(', ')} FROM session_message WHERE session_id = ?`).all(sessionId) as Record[]; + return rows.map((r) => r as unknown as SessionMessage); +} + +function readTodos(db: DatabaseSync, sessionId: string): Todo[] { + const rows = db.prepare(`SELECT ${TODO_COLUMNS.join(', ')} FROM todo WHERE session_id = ?`).all(sessionId) as Record[]; + return rows.map((r) => r as unknown as Todo); +} + +function readShares(db: DatabaseSync, sessionId: string): SessionShare[] { + const rows = db.prepare(`SELECT ${SHARE_COLUMNS.join(', ')} FROM session_share WHERE session_id = ?`).all(sessionId) as Record[]; + return rows.map((r) => r as unknown as SessionShare); +} + +export function readSessionsFromDB(dbPath: string): Session[] { + if (!fs.existsSync(dbPath)) return []; + const db = openDB(dbPath); + try { + const ids = db.prepare('SELECT id FROM session').all() as { id: string }[]; + return ids.map(({ id }) => ({ + session: readSessionMeta(db, id) as SessionMeta, + messages: readMessages(db, id), + session_messages: readSessionMessages(db, id), + todos: readTodos(db, id), + session_shares: readShares(db, id), + })); + } finally { + db.close(); + } +} + +export function readSessionsFromDir(dir: string): Session[] { + if (!fs.existsSync(dir)) return []; + const files = fs.readdirSync(dir).filter((f) => f.endsWith('.json')); + return files.map((file) => { + const content = fs.readFileSync(path.join(dir, file), 'utf-8'); + const parsed = JSON.parse(content); + return { + session: parsed.session ?? parsed, + messages: parsed.messages ?? [], + session_messages: parsed.session_messages ?? [], + todos: parsed.todos ?? [], + session_shares: parsed.session_shares ?? [], + } as Session; + }); +} + +function deleteSession(db: DatabaseSync, id: string): void { + const msgIds = db.prepare('SELECT id FROM message WHERE session_id = ?').all(id) as { id: string }[]; + for (const { id: mid } of msgIds) { + db.prepare('DELETE FROM part WHERE message_id = ?').run(mid); + } + db.prepare('DELETE FROM message WHERE session_id = ?').run(id); + db.prepare('DELETE FROM session_message WHERE session_id = ?').run(id); + db.prepare('DELETE FROM todo WHERE session_id = ?').run(id); + db.prepare('DELETE FROM session_share WHERE session_id = ?').run(id); + db.prepare('DELETE FROM session WHERE id = ?').run(id); +} + +function asSQLValue(val: unknown): string | number | null { + if (val === null || val === undefined) return null; + if (typeof val === 'string' || typeof val === 'number') return val; + return String(val); +} + +function insertSession(db: DatabaseSync, s: SessionMeta): void { + const placeholders = SESSION_COLUMNS.map(() => '?').join(', '); + const values = SESSION_COLUMNS.map((col) => asSQLValue((s as unknown as Record)[col])); + db.prepare(`INSERT INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${placeholders})`).run(...values); +} + +function insertMessages(db: DatabaseSync, messages: Message[]): void { + const placeholders = MESSAGE_COLUMNS.map(() => '?').join(', '); + const stmt = db.prepare(`INSERT INTO message (${MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + for (const msg of messages) { + stmt.run(msg.id, msg.session_id, msg.time_created, msg.time_updated, msg.data); + } +} + +function insertParts(db: DatabaseSync, parts: Part[]): void { + const placeholders = PART_COLUMNS.map(() => '?').join(', '); + const stmt = db.prepare(`INSERT INTO part (${PART_COLUMNS.join(', ')}) VALUES (${placeholders})`); + for (const part of parts) { + stmt.run(part.id, part.message_id, part.session_id, part.time_created, part.time_updated, part.data); + } +} + +function insertSessionMessages(db: DatabaseSync, items: SessionMessage[]): void { + const placeholders = SESSION_MESSAGE_COLUMNS.map(() => '?').join(', '); + const stmt = db.prepare(`INSERT INTO session_message (${SESSION_MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + for (const sm of items) { + stmt.run(sm.id, sm.session_id, sm.type, sm.time_created, sm.time_updated, sm.data); + } +} + +function insertTodos(db: DatabaseSync, items: Todo[]): void { + const placeholders = TODO_COLUMNS.map(() => '?').join(', '); + const stmt = db.prepare(`INSERT INTO todo (${TODO_COLUMNS.join(', ')}) VALUES (${placeholders})`); + for (const todo of items) { + stmt.run(todo.session_id, todo.content, todo.status, todo.priority, todo.position, todo.time_created, todo.time_updated); + } +} + +function insertShares(db: DatabaseSync, items: SessionShare[]): void { + const placeholders = SHARE_COLUMNS.map(() => '?').join(', '); + const stmt = db.prepare(`INSERT INTO session_share (${SHARE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + for (const share of items) { + stmt.run(share.session_id, share.id, share.secret, share.url, share.time_created, share.time_updated); + } +} + +export function writeSessionsToDB(dbPath: string, sessions: Session[]): void { + if (sessions.length === 0) return; + const db = openDB(dbPath); + try { + db.exec('BEGIN TRANSACTION'); + for (const s of sessions) { + const exists = db.prepare('SELECT 1 FROM session WHERE id = ?').get(s.session.id); + if (exists) { + deleteSession(db, s.session.id); + } + insertSession(db, s.session); + insertMessages(db, s.messages); + for (const msg of s.messages) { + insertParts(db, msg.parts); + } + insertSessionMessages(db, s.session_messages); + insertTodos(db, s.todos); + insertShares(db, s.session_shares); + } + db.exec('COMMIT'); + } catch (error) { + db.exec('ROLLBACK'); + throw error; + } finally { + db.close(); + } +} + +export function writeSessionsToDir(dir: string, sessions: Session[]): void { + fs.mkdirSync(dir, { recursive: true }); + for (const s of sessions) { + const filePath = path.join(dir, `${s.session.id}.json`); + fs.writeFileSync(filePath, JSON.stringify(s, null, 2), 'utf-8'); + } +} diff --git a/src/sync/session-merge.ts b/src/sync/session-merge.ts new file mode 100644 index 0000000..6c67bb1 --- /dev/null +++ b/src/sync/session-merge.ts @@ -0,0 +1,123 @@ +import type { Session, Message, Part, SessionMessage, Todo } from './session-db.js'; +import { readSessionsFromDB, readSessionsFromDir, writeSessionsToDB, writeSessionsToDir } from './session-db.js'; + +export interface SyncSessionResult { + total: number; + merged: number; + conflicts: number; +} + +function pickNewer(a: T, b: T): T { + if (!a.time_updated) return b; + if (!b.time_updated) return a; + return a.time_updated >= b.time_updated ? a : b; +} + +function unionMessages(local: Message[], remote: Message[]): Message[] { + const map = new Map(); + for (const msg of local) map.set(msg.id, msg); + for (const msg of remote) { + const existing = map.get(msg.id); + if (!existing) { + map.set(msg.id, msg); + } else { + const winner = pickNewer(existing, msg); + winner.parts = unionParts(existing.parts, msg.parts); + map.set(msg.id, winner); + } + } + return [...map.values()]; +} + +function unionParts(local: Part[], remote: Part[]): Part[] { + const map = new Map(); + for (const part of local) map.set(part.id, part); + for (const part of remote) { + const existing = map.get(part.id); + map.set(part.id, existing ? pickNewer(existing, part) : part); + } + return [...map.values()]; +} + +function unionById(local: T[], remote: T[]): T[] { + const map = new Map(); + for (const item of local) map.set(item.id, item); + for (const item of remote) { + const existing = map.get(item.id); + map.set(item.id, existing ? pickNewer(existing, item) : item); + } + return [...map.values()]; +} + +function unionTodos(local: Todo[], remote: Todo[]): Todo[] { + const map = new Map(); + for (const item of local) { + const key = `${item.session_id}:${item.content ?? ''}`; + map.set(key, item); + } + for (const item of remote) { + const key = `${item.session_id}:${item.content ?? ''}`; + const existing = map.get(key); + map.set(key, existing ? pickNewer(existing, item) : item); + } + return [...map.values()]; +} + +function merge(local: Session, remote: Session): Session { + const localT = local.session.time_updated ?? ''; + const remoteT = remote.session.time_updated ?? ''; + const mergedSession = localT >= remoteT ? local.session : remote.session; + + return { + session: mergedSession, + messages: unionMessages(local.messages, remote.messages), + session_messages: unionById(local.session_messages, remote.session_messages), + todos: unionTodos(local.todos, remote.todos), + session_shares: unionById(local.session_shares, remote.session_shares), + }; +} + +export async function syncSessions( + dbPath: string, + sessionsDir: string +): Promise { + const localSessions = readSessionsFromDB(dbPath); + const remoteSessions = readSessionsFromDir(sessionsDir); + + const localMap = new Map(); + for (const s of localSessions) localMap.set(s.session.id, s); + + const remoteMap = new Map(); + for (const s of remoteSessions) remoteMap.set(s.session.id, s); + + const allIds = new Set([...localMap.keys(), ...remoteMap.keys()]); + const merged: Session[] = []; + let mergeCount = 0; + let conflictCount = 0; + + for (const id of allIds) { + const local = localMap.get(id); + const remote = remoteMap.get(id); + + if (!local) { + merged.push(structuredClone(remote!)); + continue; + } + if (!remote) { + merged.push(structuredClone(local)); + continue; + } + + mergeCount++; + merged.push(merge(local, remote)); + } + + writeSessionsToDB(dbPath, merged); + writeSessionsToDir(sessionsDir, merged); + + return { + total: allIds.size, + merged: mergeCount, + conflicts: conflictCount, + }; +} From cd2a97f087c46b0ffc8a7cfc32ae654cc5a6e49d Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 15:59:47 +0300 Subject: [PATCH 06/17] =?UTF-8?q?feat:=20Phase=204=20=E2=80=94=20includePr?= =?UTF-8?q?ojects=20sync=20for=20opencode.global.dat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Config field includeProjects (bool, default false) - resolveProjectsFilePath(): Windows → %APPDATA%\ai.opencode.desktop\, Linux/macOS → /opencode - SyncItem with preserveWhenMissing: true for graceful handling --- src/sync/config.ts | 4 ++++ src/sync/paths.ts | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/sync/config.ts b/src/sync/config.ts index df2f6a5..9a656f6 100644 --- a/src/sync/config.ts +++ b/src/sync/config.ts @@ -61,6 +61,7 @@ export interface SyncConfig { includeModelFavorites?: boolean; includeOpencodeSkills?: boolean; includeAgentsDir?: boolean; + includeProjects?: boolean; secretsBackend?: SecretsBackendConfig; extraSecretPaths?: string[]; extraConfigPaths?: string[]; @@ -75,6 +76,7 @@ export interface NormalizedSyncConfig extends SyncConfig { includeModelFavorites: boolean; includeOpencodeSkills: boolean; includeAgentsDir: boolean; + includeProjects: boolean; secretsBackend?: SecretsBackendConfig; extraSecretPaths: string[]; extraConfigPaths: string[]; @@ -175,6 +177,7 @@ export function normalizeSyncConfig(config: SyncConfig): NormalizedSyncConfig { const includeModelFavorites = config.includeModelFavorites !== false; const includeOpencodeSkills = config.includeOpencodeSkills !== false; const includeAgentsDir = config.includeAgentsDir !== false; + const includeProjects = Boolean(config.includeProjects); return { includeSecrets, includeMcpSecrets: includeSecrets ? Boolean(config.includeMcpSecrets) : false, @@ -184,6 +187,7 @@ export function normalizeSyncConfig(config: SyncConfig): NormalizedSyncConfig { includeModelFavorites, includeOpencodeSkills, includeAgentsDir, + includeProjects, secretsBackend: normalizeSecretsBackend(config.secretsBackend), extraSecretPaths: Array.isArray(config.extraSecretPaths) ? config.extraSecretPaths : [], extraConfigPaths: Array.isArray(config.extraConfigPaths) ? config.extraConfigPaths : [], diff --git a/src/sync/paths.ts b/src/sync/paths.ts index 0ad541b..865f68d 100644 --- a/src/sync/paths.ts +++ b/src/sync/paths.ts @@ -59,6 +59,7 @@ const PROMPT_STASH_FILES = ['prompt-stash.jsonl', 'prompt-history.jsonl']; const MODEL_FAVORITES_FILE = 'model.json'; const SKILLS_DIR = 'skills'; const HOME_AGENTS_DIR = '.agents'; +const GLOBAL_DAT_FILE = 'opencode.global.dat'; export function resolveHomeDir( env: NodeJS.ProcessEnv = process.env, @@ -162,6 +163,18 @@ export function resolveRepoRoot(config: SyncConfig | null, locations: SyncLocati return locations.defaultRepoDir; } +export function resolveProjectsFilePath( + env: NodeJS.ProcessEnv = process.env, + platform: NodeJS.Platform = process.platform +): string { + if (platform === 'win32') { + const appData = env.APPDATA ?? path.join(env.USERPROFILE ?? '', 'AppData', 'Roaming'); + return path.join(appData, 'ai.opencode.desktop', GLOBAL_DAT_FILE); + } + const dataDir = env.XDG_DATA_HOME ?? path.join(resolveHomeDir(env, platform), '.local', 'share'); + return path.join(dataDir, 'opencode', GLOBAL_DAT_FILE); +} + export function buildSyncPlan( config: NormalizedSyncConfig, locations: SyncLocations, @@ -240,6 +253,17 @@ export function buildSyncPlan( }); } + if (config.includeProjects) { + items.push({ + localPath: resolveProjectsFilePath(process.env, platform), + repoPath: path.join(repoDataRoot, GLOBAL_DAT_FILE), + type: 'file', + isSecret: false, + isConfigFile: false, + preserveWhenMissing: true, + }); + } + if (config.includeSecrets) { if (!usingSecretsBackend) { items.push( From 2d7c51fefc12279050782ef0cf2fa0a956098c8b Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 21:28:39 +0300 Subject: [PATCH 07/17] fix: add createNodeShell() fallback for Desktop where ctx.$ is undefined - shell.ts: createNodeShell() wraps child_process.execSync with Bun-compatible tagged-template API - service.ts: detect ctx.$ === undefined at createSyncService entry, replace with createNodeShell() - Enables gh/git commands in OpenCode Desktop (Electron/Node.js) --- src/sync/service.ts | 5 +++ src/sync/shell.ts | 83 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/sync/shell.ts diff --git a/src/sync/service.ts b/src/sync/service.ts index 65c81b1..e14b07d 100644 --- a/src/sync/service.ts +++ b/src/sync/service.ts @@ -49,6 +49,7 @@ import { type TursoSyncPreference, } from './turso.js'; import { syncSessions } from './session-merge.js'; +import { createNodeShell } from './shell.js'; import { createLogger, extractTextFromResponse, @@ -116,6 +117,10 @@ export interface SyncService { } export function createSyncService(ctx: SyncServiceContext): SyncService { + if (!ctx.$) { + ctx.$ = createNodeShell() as unknown as SyncServiceContext['$']; + } + const locations = resolveSyncLocations(); const log = createLogger(ctx.client); const lockPath = path.join(path.dirname(locations.statePath), 'sync.lock'); diff --git a/src/sync/shell.ts b/src/sync/shell.ts new file mode 100644 index 0000000..8a21cdc --- /dev/null +++ b/src/sync/shell.ts @@ -0,0 +1,83 @@ +import { execSync } from 'node:child_process'; + +export interface ShellProcessPromise extends Promise<{ exitCode: number; stdout: string; stderr: string }> { + quiet(): this; + text(): Promise; +} + +export type ShellFn = (strings: TemplateStringsArray, ...values: unknown[]) => ShellProcessPromise; + +function buildCommand(strings: TemplateStringsArray, ...values: unknown[]): string { + let cmd = ''; + for (let i = 0; i < strings.length; i++) { + cmd += strings[i]; + if (i < values.length) { + const v = values[i]; + cmd += typeof v === 'string' ? v : String(v); + } + } + return cmd.trim(); +} + +export function createNodeShell(): ShellFn { + return (strings: TemplateStringsArray, ...values: unknown[]): ShellProcessPromise => { + const command = buildCommand(strings, ...values); + let isQuiet = false; + + const run = (): { exitCode: number; stdout: string; stderr: string } => { + try { + const stdout = execSync(command, { encoding: 'utf-8', stdio: isQuiet ? 'pipe' : 'inherit' }); + return { exitCode: 0, stdout: stdout ?? '', stderr: '' }; + } catch (error) { + if (error instanceof Error && 'stdout' in error && 'stderr' in error) { + const execError = error as Error & { stdout: string; stderr: string; status?: number }; + return { + exitCode: execError.status ?? 1, + stdout: execError.stdout ?? '', + stderr: execError.stderr ?? '', + }; + } + throw error; + } + }; + + let promise: Promise<{ exitCode: number; stdout: string; stderr: string }> | null = null; + const getPromise = (): Promise<{ exitCode: number; stdout: string; stderr: string }> => { + if (!promise) { + promise = new Promise((resolve) => { + resolve(run()); + }); + } + return promise; + }; + + const shellPromise = { + then( + onfulfilled?: ((value: { exitCode: number; stdout: string; stderr: string }) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): Promise { + return getPromise().then(onfulfilled, onrejected); + }, + catch( + onrejected?: ((reason: unknown) => TResult | PromiseLike) | null + ): Promise<{ exitCode: number; stdout: string; stderr: string } | TResult> { + return getPromise().catch(onrejected); + }, + finally(onfinally?: (() => void) | null): Promise<{ exitCode: number; stdout: string; stderr: string }> { + return getPromise().finally(onfinally); + }, + get [Symbol.toStringTag]() { + return 'ShellProcessPromise'; + }, + quiet(): ShellProcessPromise { + isQuiet = true; + return shellPromise as unknown as ShellProcessPromise; + }, + text(): Promise { + return getPromise().then((r) => r.stdout); + }, + } as ShellProcessPromise; + + return shellPromise; + }; +} From cca551373e213d12ca36e9e9d4fc70f55c02058f Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 21:38:30 +0300 Subject: [PATCH 08/17] fix: wrap all SQLite insert params with asSQLValue() to handle undefined/objects - All insert* functions now pass values through v() = asSQLValue() - asSQLValue() now also serializes objects/arrays via JSON.stringify - Fixes 'Provided value cannot be bound to SQLite parameter' on startup --- AGENTS.md | 3 +++ src/sync/session-db.ts | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fd62585..e991ac1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,8 @@ # AGENTS.md +## Language +- Все ответы пользователю — ТОЛЬКО на русском языке. Никогда не использовать английский. + ## Build & Test Commands (Windows / Node.js) - **Build**: `npx tsc -p tsconfig.build.json && copyfiles -u 1 "src/command/**/*" dist/` diff --git a/src/sync/session-db.ts b/src/sync/session-db.ts index 4c3a809..b07795c 100644 --- a/src/sync/session-db.ts +++ b/src/sync/session-db.ts @@ -186,8 +186,10 @@ function deleteSession(db: DatabaseSync, id: string): void { function asSQLValue(val: unknown): string | number | null { if (val === null || val === undefined) return null; - if (typeof val === 'string' || typeof val === 'number') return val; - return String(val); + if (typeof val === 'string') return val; + if (typeof val === 'number') return val; + if (typeof val === 'boolean') return val ? 1 : 0; + return JSON.stringify(val); } function insertSession(db: DatabaseSync, s: SessionMeta): void { @@ -196,11 +198,15 @@ function insertSession(db: DatabaseSync, s: SessionMeta): void { db.prepare(`INSERT INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${placeholders})`).run(...values); } +function v(val: unknown): string | number | null { + return asSQLValue(val); +} + function insertMessages(db: DatabaseSync, messages: Message[]): void { const placeholders = MESSAGE_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO message (${MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const msg of messages) { - stmt.run(msg.id, msg.session_id, msg.time_created, msg.time_updated, msg.data); + stmt.run(v(msg.id), v(msg.session_id), v(msg.time_created), v(msg.time_updated), v(msg.data)); } } @@ -208,7 +214,7 @@ function insertParts(db: DatabaseSync, parts: Part[]): void { const placeholders = PART_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO part (${PART_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const part of parts) { - stmt.run(part.id, part.message_id, part.session_id, part.time_created, part.time_updated, part.data); + stmt.run(v(part.id), v(part.message_id), v(part.session_id), v(part.time_created), v(part.time_updated), v(part.data)); } } @@ -216,7 +222,7 @@ function insertSessionMessages(db: DatabaseSync, items: SessionMessage[]): void const placeholders = SESSION_MESSAGE_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO session_message (${SESSION_MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const sm of items) { - stmt.run(sm.id, sm.session_id, sm.type, sm.time_created, sm.time_updated, sm.data); + stmt.run(v(sm.id), v(sm.session_id), v(sm.type), v(sm.time_created), v(sm.time_updated), v(sm.data)); } } @@ -224,7 +230,7 @@ function insertTodos(db: DatabaseSync, items: Todo[]): void { const placeholders = TODO_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO todo (${TODO_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const todo of items) { - stmt.run(todo.session_id, todo.content, todo.status, todo.priority, todo.position, todo.time_created, todo.time_updated); + stmt.run(v(todo.session_id), v(todo.content), v(todo.status), v(todo.priority), v(todo.position), v(todo.time_created), v(todo.time_updated)); } } @@ -232,7 +238,7 @@ function insertShares(db: DatabaseSync, items: SessionShare[]): void { const placeholders = SHARE_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO session_share (${SHARE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const share of items) { - stmt.run(share.session_id, share.id, share.secret, share.url, share.time_created, share.time_updated); + stmt.run(v(share.session_id), v(share.id), v(share.secret), v(share.url), v(share.time_created), v(share.time_updated)); } } From e53b7519c9a71dc71af77d791386303640404888 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 21:57:18 +0300 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20merge-=D0=BB=D0=BE=D0=B3=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D0=BB=D1=8F=20opencode.global.dat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit projects-merge.ts: syncGlobalData() — union-merge проектов * server.projects.local — объединение по worktree * layout.page.lastProjectSession — объединение по директории, новее at * остальные ключи — если нет в локальном, берётся с удалённого service.ts: вызов syncGlobalData в pull/push/startup paths.ts: удалён старый SyncItem для простого копирования --- src/sync/paths.ts | 11 --- src/sync/projects-merge.ts | 154 +++++++++++++++++++++++++++++++++++++ src/sync/service.ts | 19 ++++- 3 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 src/sync/projects-merge.ts diff --git a/src/sync/paths.ts b/src/sync/paths.ts index 865f68d..06e4d8c 100644 --- a/src/sync/paths.ts +++ b/src/sync/paths.ts @@ -253,17 +253,6 @@ export function buildSyncPlan( }); } - if (config.includeProjects) { - items.push({ - localPath: resolveProjectsFilePath(process.env, platform), - repoPath: path.join(repoDataRoot, GLOBAL_DAT_FILE), - type: 'file', - isSecret: false, - isConfigFile: false, - preserveWhenMissing: true, - }); - } - if (config.includeSecrets) { if (!usingSecretsBackend) { items.push( diff --git a/src/sync/projects-merge.ts b/src/sync/projects-merge.ts new file mode 100644 index 0000000..df6c117 --- /dev/null +++ b/src/sync/projects-merge.ts @@ -0,0 +1,154 @@ +import fs from 'node:fs'; + +export interface ProjectEntry { + worktree: string; + expanded?: boolean; +} + +export interface ServerData { + list: unknown[]; + projects: { + local: ProjectEntry[]; + }; + lastProject?: { local?: string }; +} + +export interface LastProjectSessionEntry { + directory: string; + id: string; + at: number; +} + +export interface LayoutPageData { + lastProjectSession?: Record; +} + +export interface GlobalData { + [key: string]: string; +} + +function readGlobalData(filePath: string): GlobalData | null { + try { + const raw = fs.readFileSync(filePath, 'utf-8'); + return JSON.parse(raw) as GlobalData; + } catch { + return null; + } +} + +function writeGlobalData(filePath: string, data: GlobalData): void { + fs.writeFileSync(filePath, JSON.stringify(data, null, '\t'), 'utf-8'); +} + +function parseServer(raw: string | undefined): ServerData | null { + if (!raw) return null; + try { + return JSON.parse(raw) as ServerData; + } catch { + return null; + } +} + +function parseLayoutPage(raw: string | undefined): LayoutPageData | null { + if (!raw) return null; + try { + return JSON.parse(raw) as LayoutPageData; + } catch { + return null; + } +} + +function unionProjects(local: ProjectEntry[], remote: ProjectEntry[]): ProjectEntry[] { + const map = new Map(); + for (const p of local) map.set(p.worktree.toLowerCase(), p); + for (const p of remote) { + const key = p.worktree.toLowerCase(); + if (!map.has(key)) { + map.set(key, p); + } + } + return [...map.values()]; +} + +function unionLastSession( + local: Record | undefined, + remote: Record | undefined +): Record { + const map = new Map(); + if (local) { + for (const [dir, entry] of Object.entries(local)) { + map.set(dir.toLowerCase(), entry); + } + } + if (remote) { + for (const [dir, entry] of Object.entries(remote)) { + const key = dir.toLowerCase(); + const existing = map.get(key); + if (!existing || entry.at > existing.at) { + map.set(key, entry); + } + } + } + const result: Record = {}; + for (const [_, entry] of map) { + result[entry.directory] = entry; + } + return result; +} + +export function syncGlobalData(localPath: string, remotePath: string): boolean { + const local = readGlobalData(localPath); + const remote = readGlobalData(remotePath); + + if (!local && !remote) return false; + if (!local) { + if (remote) writeGlobalData(localPath, remote); + return true; + } + if (!remote) { + writeGlobalData(remotePath, local); + return true; + } + + const merged: GlobalData = { ...local }; + + for (const key of Object.keys(remote)) { + if (key === 'server') { + const localServer = parseServer(local[key]); + const remoteServer = parseServer(remote[key]); + if (localServer && remoteServer) { + const mergedProjects = unionProjects( + localServer.projects?.local ?? [], + remoteServer.projects?.local ?? [] + ); + merged[key] = JSON.stringify({ + ...localServer, + projects: { local: mergedProjects }, + }); + } else { + merged[key] = local[key] ?? remote[key]; + } + } else if (key === 'layout.page') { + const localLayout = parseLayoutPage(local[key]); + const remoteLayout = parseLayoutPage(remote[key]); + if (localLayout && remoteLayout) { + const mergedSessions = unionLastSession( + localLayout.lastProjectSession, + remoteLayout.lastProjectSession + ); + merged[key] = JSON.stringify({ + ...localLayout, + lastProjectSession: mergedSessions, + }); + } else { + merged[key] = local[key] ?? remote[key]; + } + } else if (!(key in local)) { + merged[key] = remote[key]; + } + } + + writeGlobalData(localPath, merged); + writeGlobalData(remotePath, merged); + return true; +} diff --git a/src/sync/service.ts b/src/sync/service.ts index e14b07d..f63c513 100644 --- a/src/sync/service.ts +++ b/src/sync/service.ts @@ -19,7 +19,7 @@ import { import { SyncCommandError, SyncConfigMissingError } from './errors.js'; import type { SyncLockInfo } from './lock.js'; import { withSyncLock } from './lock.js'; -import { buildSyncPlan, resolveRepoRoot, resolveSyncLocations } from './paths.js'; +import { buildSyncPlan, resolveProjectsFilePath, resolveRepoRoot, resolveSyncLocations } from './paths.js'; import { commitAll, ensureRepoCloned, @@ -49,6 +49,7 @@ import { type TursoSyncPreference, } from './turso.js'; import { syncSessions } from './session-merge.js'; +import { syncGlobalData } from './projects-merge.js'; import { createNodeShell } from './shell.js'; import { createLogger, @@ -963,6 +964,10 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); } + if (config.includeProjects) { + syncGlobalData(resolveProjectsFilePath(), path.join(repoRoot, 'data', 'opencode.global.dat')); + } + await updateState(locations, { lastPull: new Date().toISOString(), lastRemoteUpdate: new Date().toISOString(), @@ -1008,6 +1013,10 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); } + if (config.includeProjects) { + syncGlobalData(resolveProjectsFilePath(), path.join(repoRoot, 'data', 'opencode.global.dat')); + } + await syncLocalToRepo(plan, overrides, { overridesPath: locations.overridesPath, allowMcpSecrets: canCommitMcpSecrets(config), @@ -1418,6 +1427,10 @@ async function runStartup( log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); } + if (config.includeProjects) { + syncGlobalData(resolveProjectsFilePath(), path.join(repoRoot, 'data', 'opencode.global.dat')); + } + await updateState(locations, { lastPull: new Date().toISOString(), lastRemoteUpdate: new Date().toISOString(), @@ -1436,6 +1449,10 @@ async function runStartup( log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); } + if (config.includeProjects) { + syncGlobalData(resolveProjectsFilePath(), path.join(repoRoot, 'data', 'opencode.global.dat')); + } + await syncLocalToRepo(plan, overrides, { overridesPath: locations.overridesPath, allowMcpSecrets: canCommitMcpSecrets(config), From 4e9e1615a60749e2f8796a71eafd2813386ff315 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 22:06:48 +0300 Subject: [PATCH 10/17] fix: skip writeSessionsToDB when remote is empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit При пустой директории remoteSessions (первый запуск, нет удалённых сессий) syncSessions пишет только JSON-файлы, не перезаписывая локальную БД. Это избегает ошибки 'Provided value cannot be bound to SQLite parameter 1', которая возникала из-за NOT NULL constraint в таблицах session/message/part при DELETE+INSERT --- src/sync/session-db.ts | 44 ++++++++++++++++++++++++++++----------- src/sync/session-merge.ts | 8 +++++-- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/sync/session-db.ts b/src/sync/session-db.ts index b07795c..8279775 100644 --- a/src/sync/session-db.ts +++ b/src/sync/session-db.ts @@ -173,15 +173,19 @@ export function readSessionsFromDir(dir: string): Session[] { } function deleteSession(db: DatabaseSync, id: string): void { - const msgIds = db.prepare('SELECT id FROM message WHERE session_id = ?').all(id) as { id: string }[]; - for (const { id: mid } of msgIds) { - db.prepare('DELETE FROM part WHERE message_id = ?').run(mid); + try { + const msgIds = db.prepare('SELECT id FROM message WHERE session_id = ?').all(id) as { id: string }[]; + for (const { id: mid } of msgIds) { + db.prepare('DELETE FROM part WHERE message_id = ?').run(mid); + } + db.prepare('DELETE FROM message WHERE session_id = ?').run(id); + db.prepare('DELETE FROM session_message WHERE session_id = ?').run(id); + db.prepare('DELETE FROM todo WHERE session_id = ?').run(id); + db.prepare('DELETE FROM session_share WHERE session_id = ?').run(id); + db.prepare('DELETE FROM session WHERE id = ?').run(id); + } catch (e) { + throw new Error(`deleteSession failed for session ${id}: ${e}`); } - db.prepare('DELETE FROM message WHERE session_id = ?').run(id); - db.prepare('DELETE FROM session_message WHERE session_id = ?').run(id); - db.prepare('DELETE FROM todo WHERE session_id = ?').run(id); - db.prepare('DELETE FROM session_share WHERE session_id = ?').run(id); - db.prepare('DELETE FROM session WHERE id = ?').run(id); } function asSQLValue(val: unknown): string | number | null { @@ -195,7 +199,11 @@ function asSQLValue(val: unknown): string | number | null { function insertSession(db: DatabaseSync, s: SessionMeta): void { const placeholders = SESSION_COLUMNS.map(() => '?').join(', '); const values = SESSION_COLUMNS.map((col) => asSQLValue((s as unknown as Record)[col])); - db.prepare(`INSERT INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${placeholders})`).run(...values); + try { + db.prepare(`INSERT INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${placeholders})`).run(...values); + } catch (e) { + throw new Error(`insertSession failed for session ${s.id}: ${e}`); + } } function v(val: unknown): string | number | null { @@ -206,7 +214,11 @@ function insertMessages(db: DatabaseSync, messages: Message[]): void { const placeholders = MESSAGE_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO message (${MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const msg of messages) { - stmt.run(v(msg.id), v(msg.session_id), v(msg.time_created), v(msg.time_updated), v(msg.data)); + try { + stmt.run(v(msg.id), v(msg.session_id), v(msg.time_created), v(msg.time_updated), v(msg.data)); + } catch (e) { + throw new Error(`insertMessages failed for msg ${msg.id}: ${e}`); + } } } @@ -214,7 +226,11 @@ function insertParts(db: DatabaseSync, parts: Part[]): void { const placeholders = PART_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO part (${PART_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const part of parts) { - stmt.run(v(part.id), v(part.message_id), v(part.session_id), v(part.time_created), v(part.time_updated), v(part.data)); + try { + stmt.run(v(part.id), v(part.message_id), v(part.session_id), v(part.time_created), v(part.time_updated), v(part.data)); + } catch (e) { + throw new Error(`insertParts failed for part ${part.id}: ${e}`); + } } } @@ -222,7 +238,11 @@ function insertSessionMessages(db: DatabaseSync, items: SessionMessage[]): void const placeholders = SESSION_MESSAGE_COLUMNS.map(() => '?').join(', '); const stmt = db.prepare(`INSERT INTO session_message (${SESSION_MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const sm of items) { - stmt.run(v(sm.id), v(sm.session_id), v(sm.type), v(sm.time_created), v(sm.time_updated), v(sm.data)); + try { + stmt.run(v(sm.id), v(sm.session_id), v(sm.type), v(sm.time_created), v(sm.time_updated), v(sm.data)); + } catch (e) { + throw new Error(`insertSessionMessages failed for sm ${sm.id}: ${e}`); + } } } diff --git a/src/sync/session-merge.ts b/src/sync/session-merge.ts index 6c67bb1..e31cd0a 100644 --- a/src/sync/session-merge.ts +++ b/src/sync/session-merge.ts @@ -112,8 +112,12 @@ export async function syncSessions( merged.push(merge(local, remote)); } - writeSessionsToDB(dbPath, merged); - writeSessionsToDir(sessionsDir, merged); + if (remoteSessions.length === 0) { + writeSessionsToDir(sessionsDir, merged); + } else { + writeSessionsToDB(dbPath, merged); + writeSessionsToDir(sessionsDir, merged); + } return { total: allIds.size, From 8836dbc9fe0749da63f68b8ff70823eea5d8e4a3 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 22:17:28 +0300 Subject: [PATCH 11/17] fix: use INSERT OR REPLACE instead of DELETE+INSERT in writeSessionsToDB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Убирает deleteSession + каскадное удаление - Все insert* функции заменены на upsert* (INSERT OR REPLACE) - Избегает NOT NULL constraint violations при перезаписи сессий - Предотвращает ошибку 'Provided value cannot be bound to SQLite parameter 1' --- src/sync/session-db.ts | 61 ++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/sync/session-db.ts b/src/sync/session-db.ts index 8279775..7669b54 100644 --- a/src/sync/session-db.ts +++ b/src/sync/session-db.ts @@ -196,67 +196,62 @@ function asSQLValue(val: unknown): string | number | null { return JSON.stringify(val); } -function insertSession(db: DatabaseSync, s: SessionMeta): void { - const placeholders = SESSION_COLUMNS.map(() => '?').join(', '); +function upsertSession(db: DatabaseSync, s: SessionMeta): void { const values = SESSION_COLUMNS.map((col) => asSQLValue((s as unknown as Record)[col])); try { - db.prepare(`INSERT INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${placeholders})`).run(...values); + db.prepare(`INSERT OR REPLACE INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${SESSION_COLUMNS.map(() => '?').join(',')})`).run(...values); } catch (e) { - throw new Error(`insertSession failed for session ${s.id}: ${e}`); + throw new Error(`upsertSession failed for session ${s.id}: ${e}`); } } -function v(val: unknown): string | number | null { - return asSQLValue(val); -} - -function insertMessages(db: DatabaseSync, messages: Message[]): void { +function upsertMessages(db: DatabaseSync, messages: Message[]): void { const placeholders = MESSAGE_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT INTO message (${MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare(`INSERT OR REPLACE INTO message (${MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const msg of messages) { try { stmt.run(v(msg.id), v(msg.session_id), v(msg.time_created), v(msg.time_updated), v(msg.data)); } catch (e) { - throw new Error(`insertMessages failed for msg ${msg.id}: ${e}`); + throw new Error(`upsertMessages failed for msg ${msg.id}: ${e}`); } } } -function insertParts(db: DatabaseSync, parts: Part[]): void { +function upsertParts(db: DatabaseSync, parts: Part[]): void { const placeholders = PART_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT INTO part (${PART_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare(`INSERT OR REPLACE INTO part (${PART_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const part of parts) { try { stmt.run(v(part.id), v(part.message_id), v(part.session_id), v(part.time_created), v(part.time_updated), v(part.data)); } catch (e) { - throw new Error(`insertParts failed for part ${part.id}: ${e}`); + throw new Error(`upsertParts failed for part ${part.id}: ${e}`); } } } -function insertSessionMessages(db: DatabaseSync, items: SessionMessage[]): void { +function v(val: unknown): string | number | null { + return asSQLValue(val); +} + +function upsertSessionMessages(db: DatabaseSync, items: SessionMessage[]): void { const placeholders = SESSION_MESSAGE_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT INTO session_message (${SESSION_MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare(`INSERT OR REPLACE INTO session_message (${SESSION_MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const sm of items) { - try { - stmt.run(v(sm.id), v(sm.session_id), v(sm.type), v(sm.time_created), v(sm.time_updated), v(sm.data)); - } catch (e) { - throw new Error(`insertSessionMessages failed for sm ${sm.id}: ${e}`); - } + stmt.run(v(sm.id), v(sm.session_id), v(sm.type), v(sm.time_created), v(sm.time_updated), v(sm.data)); } } -function insertTodos(db: DatabaseSync, items: Todo[]): void { +function upsertTodos(db: DatabaseSync, items: Todo[]): void { const placeholders = TODO_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT INTO todo (${TODO_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare(`INSERT OR REPLACE INTO todo (${TODO_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const todo of items) { stmt.run(v(todo.session_id), v(todo.content), v(todo.status), v(todo.priority), v(todo.position), v(todo.time_created), v(todo.time_updated)); } } -function insertShares(db: DatabaseSync, items: SessionShare[]): void { +function upsertShares(db: DatabaseSync, items: SessionShare[]): void { const placeholders = SHARE_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT INTO session_share (${SHARE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare(`INSERT OR REPLACE INTO session_share (${SHARE_COLUMNS.join(', ')}) VALUES (${placeholders})`); for (const share of items) { stmt.run(v(share.session_id), v(share.id), v(share.secret), v(share.url), v(share.time_created), v(share.time_updated)); } @@ -268,18 +263,14 @@ export function writeSessionsToDB(dbPath: string, sessions: Session[]): void { try { db.exec('BEGIN TRANSACTION'); for (const s of sessions) { - const exists = db.prepare('SELECT 1 FROM session WHERE id = ?').get(s.session.id); - if (exists) { - deleteSession(db, s.session.id); - } - insertSession(db, s.session); - insertMessages(db, s.messages); + upsertSession(db, s.session); + upsertMessages(db, s.messages); for (const msg of s.messages) { - insertParts(db, msg.parts); + upsertParts(db, msg.parts); } - insertSessionMessages(db, s.session_messages); - insertTodos(db, s.todos); - insertShares(db, s.session_shares); + upsertSessionMessages(db, s.session_messages); + upsertTodos(db, s.todos); + upsertShares(db, s.session_shares); } db.exec('COMMIT'); } catch (error) { From df2be57d1755e43d4b7340ba3e81cfc4b8606311 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 22:23:50 +0300 Subject: [PATCH 12/17] fix: handle old-format session JSON files in readSessionsFromDir --- src/sync/session-db.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/sync/session-db.ts b/src/sync/session-db.ts index 7669b54..f1f8562 100644 --- a/src/sync/session-db.ts +++ b/src/sync/session-db.ts @@ -156,12 +156,42 @@ export function readSessionsFromDB(dbPath: string): Session[] { } } +function arrayToObject(arr: unknown[], colNames: string[]): Record { + const obj: Record = {}; + for (let i = 0; i < colNames.length; i++) { + obj[colNames[i]] = arr[i] ?? null; + } + return obj; +} + export function readSessionsFromDir(dir: string): Session[] { if (!fs.existsSync(dir)) return []; const files = fs.readdirSync(dir).filter((f) => f.endsWith('.json')); return files.map((file) => { const content = fs.readFileSync(path.join(dir, file), 'utf-8'); const parsed = JSON.parse(content); + + if (Array.isArray(parsed.session) && parsed.columns?.session) { + const session = arrayToObject(parsed.session, parsed.columns.session); + const messages = (parsed.message ?? []).map((row: unknown[]) => { + const msg = arrayToObject(row, parsed.columns.message); + (msg as Record).parts = (parsed.parts ?? []) + .filter((p: unknown[]) => p[1] === msg.id) + .map((p: unknown[]) => arrayToObject(p, parsed.columns.parts)); + return msg; + }); + const session_messages = (parsed.session_messages ?? []).map((row: unknown[]) => + arrayToObject(row, parsed.columns.session_message) + ); + return { + session: session as unknown as SessionMeta, + messages, + session_messages, + todos: [], + session_shares: [], + } as Session; + } + return { session: parsed.session ?? parsed, messages: parsed.messages ?? [], From b1d131063ec42929617c835a290153afe7976ec9 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Fri, 22 May 2026 22:39:42 +0300 Subject: [PATCH 13/17] fix: remove full DB copy and storage dirs from sync plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Убрано копирование opencode.db и storage/* (SESSION_DIRS) - Сессии синхронизируются только через per-session JSON merge (syncSessions) - Предотвращает затирание локальных сессий при pull --- src/sync/paths.ts | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/sync/paths.ts b/src/sync/paths.ts index 06e4d8c..c7605aa 100644 --- a/src/sync/paths.ts +++ b/src/sync/paths.ts @@ -274,25 +274,8 @@ export function buildSyncPlan( } if (config.includeSessions && !isTursoSessionBackend(config)) { - items.push({ - localPath: path.join(dataRoot, SESSION_DB_FILE), - repoPath: path.join(repoDataRoot, SESSION_DB_FILE), - type: 'file', - isSecret: true, - isConfigFile: false, - preserveWhenMissing: true, - }); - - for (const dirName of SESSION_DIRS) { - items.push({ - localPath: path.join(dataRoot, dirName), - repoPath: path.join(repoDataRoot, dirName), - type: 'dir', - isSecret: true, - isConfigFile: false, - preserveWhenMissing: true, - }); - } + // Session sync uses per-session JSON merge (syncSessions), + // NOT raw DB/storage copy — to avoid overwriting local sessions on pull. } if (config.includePromptStash) { From 707b8d8c698812f996e3a13a9f04ea34edcbeab3 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Sat, 23 May 2026 10:10:10 +0300 Subject: [PATCH 14/17] chore: lint fixes, error handling polish, production readiness --- AGENTS.md | 111 ++++---- package.json | 8 +- src/command/sync-enable-secrets.md | 15 +- src/command/sync-init.md | 2 +- src/command/sync-link.md | 2 +- src/command/sync-pull.md | 10 +- src/command/sync-push.md | 11 +- src/command/sync-resolve.md | 10 +- src/command/sync-secrets-pull.md | 9 +- src/command/sync-secrets-push.md | 9 +- src/command/sync-secrets-status.md | 9 +- src/command/sync-sessions-backend.md | 3 +- src/command/sync-sessions-cleanup-git.md | 7 +- src/command/sync-sessions-migrate-turso.md | 3 +- src/command/sync-sessions-setup-turso.md | 3 +- src/command/sync-status.md | 9 +- src/index.ts | 18 +- src/sync/apply.ts | 89 +++++- src/sync/commit.ts | 4 +- src/sync/config.ts | 1 + src/sync/paths.test.ts | 20 +- src/sync/paths.ts | 101 +++---- src/sync/repo.ts | 9 + src/sync/service.ts | 162 +++++++---- src/sync/session-db.ts | 299 +++++++++++++++++---- src/sync/session-merge.ts | 106 +++++--- src/sync/shell.ts | 66 +++-- src/sync/turso.test.ts | 12 +- 28 files changed, 790 insertions(+), 318 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e991ac1..d458913 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,59 +82,60 @@ - Session DB: `C:\Users\Aleks\.local\share\opencode\opencode.db` (SQLite) - Desktop projects store: `%APPDATA%\ai.opencode.desktop\opencode.global.dat` -## FORK PLAN — 6 Phases - -### Phase 0 ✅ DONE -- [x] Clone latest `main` from `github.com/iHildy/opencode-synced` (commit a627673) -- [x] Fix `package.json`: `@opencode-ai/plugin` → local `file:` reference in devDependencies -- [x] `npm install`, `npx tsc -p tsconfig.build.json` — build passes -- [ ] ~~`bun test`~~ → use `npx vitest run` (no bun on Windows) - -### Phase 1: path.ts — XDG on all platforms (remove win32 APPDATA) -**Files**: `src/sync/paths.ts`, `src/sync/paths.test.ts` -1. Remove `if (platform === 'win32')` block with `env.APPDATA`/`env.LOCALAPPDATA` -2. `dataDir` = `env.XDG_DATA_HOME ?? path.join(home, '.local', 'share')` -3. `configDir` = `env.XDG_CONFIG_HOME ?? path.join(home, '.config')` -4. Fix `env.opencode_config_dir` → `env.OPENCODE_CONFIG_DIR` -5. Update tests for Windows expectations (no more `%APPDATA%`) - -### Phase 2: Notifications — toasts + logs on key lifecycle points -**Files**: `src/sync/utils.ts`, `src/sync/service.ts` -1. Add `notify(client, { emoji, title, message, variant })` — unified entry point -2. `runStartup()` → `"🔄 Sync starting…"`, `"✅ opencode-synced ready"` -3. `pull()` → `"📥 Pulling…"`, `"📥 Pull complete — X changes"` -4. `push()` → `"📤 Pushing…"`, `"📤 Push successful"` -5. `link()` → `"🔗 Linking…"`, `"🔗 Linked to {repo}"` -6. `syncSessions()` → `"💾 Syncing sessions…"`, result summary -7. Errors → `"❌ {message}"` with variant='error' - -### Phase 3: Session sync — union-merge per record -**New file**: `src/sync/session-merge.ts` -**Edit**: `src/sync/session-db.ts`, `src/sync/service.ts` - -**Problem**: Current `exportSessions` / `importSessions` use `skip-if-exists` — updated sessions are never re-synced. -**Solution**: Replace with `syncSessions(localDB, remoteDir)`: -1. Read ALL sessions from local SQLite → `Map` -2. Read ALL session .json files from repo → `Map` -3. For each unique id: `merge(local, remote)` per record: - - session meta: newer `time_updated` wins - - messages: union by `message.id`, each picks max `time_updated` - - parts: union by `part.id` nested in messages - - todos, session_messages, shares: union by id -4. Write merged to both sides (SQLite + .json) -5. Called both on pull (after fetchAndFastForward) and push (before syncLocalToRepo) - -### Phase 4: includeProjects — sync opencode.global.dat -**Files**: `src/sync/config.ts`, `src/sync/service.ts` -1. Add `includeProjects: boolean = false` config field -2. Read `opencode.global.dat` from Desktop AppData -3. Copy to `data/opencode.global.dat` in repo on push -4. Copy from repo on pull -5. Warn: Desktop restart required to apply projects - -### Phase 5: Build, deploy, test -1. `npx tsc -p tsconfig.build.json && copyfiles -u 1 "src/command/**/*" dist/` -2. Copy `dist/` → `C:\Users\Aleks\.config\opencode\plugins\opencode-synced\` +## Build Output Notes + +- **compact JSON**: All session files use `JSON.stringify(session)` without `null, 2` (pretty-print). This saves ~30% disk space and reduces git diff noise. Reading both formats is supported (single-session and old columnar). +- **Broken JSON recovery**: If a session JSON file can't be parsed, a `.broken` backup is created before returning null (prevents total data loss). + +## Cross-Platform (Windows / Linux / macOS) + +- `platformJoin(platform, ...parts)` in `paths.ts` — uses `path.posix.join` or `path.win32.join` based on `platform` parameter +- `expandHome()` and `normalizePath()` — always produce forward slashes, backslashes normalized before comparison +- All `buildSyncPlan`/`resolveXdgPaths`/`resolveSyncLocations` tests pass on Windows (79/79) + +## Project State (23.05.2026) + +### Done +- XDG paths on all platforms (no %APPDATA%) +- Notifications (toasts + logs) on key lifecycle points +- Session sync: union-merge per record (session-db.ts, session-merge.ts) +- Projects sync: opencode.global.dat merge (projects-merge.ts) +- Auto-commit pending changes on startup instead of bailing out +- Removed AI commit message generation → date-based messages +- `createNodeShell()`: async `exec` instead of `execSync`, proper arg quoting +- CONFIG_DIRS includes `'plugins'` and `'commands'` for Desktop compatibility +- HEAD cache skip: если HEAD не изменился и нет локальных изменений — пропускаем git fetch (экономия сети) +- skipIfBusy: file-based exclusive lock предотвращает конкурентные sync +- Дебаунс: startup sync обёрнут в skipIfBusy +- Двуязычные комментарии EN/RU в ключевых модулях +- Двуязычные `description` в 14 `.md` командах +- `parseFrontmatter()` — BOM + CRLF + fallback first-line fix +- `platformJoin()` helper для кроссплатформенных тестов +- `expandHome()` — всегда forward slashes (не `path.join`) +- Все 79 тестов проходят на Windows (0 known failures) +- `asSQLValue()` — обёртка всех SQLite-параметров +- `INSERT OR REPLACE` вместо DELETE+INSERT +- Пропуск `writeSessionsToDB` при пустой remote-директории +- `readSessionsFromDir` — поддержка старого колоночного формата JSON +- Убрано копирование `opencode.db` и `storage/` из sync plan (сессии → syncSessions) + +### Session Sync Optimizations +1. **Compact JSON** — `JSON.stringify(session)` без `null, 2` (экономия ~30%) +2. **WAL checkpoint один раз** — вынесен из `openDB()` в `syncSessions()`, однакратно за цикл +3. **Background git push** — `pushBranch(...).catch(...)`, не блокирует стартап +4. **Единый DB handle** — `syncSessions()` открывает SQLite один раз, читает/пишет через handle-варианты функций +5. **Batch DB writes** — все мержнутые сессии пишутся одним `BEGIN…COMMIT`, а не по одной +6. **Recovery битых JSON** — перед возвратом `null` создаётся `.broken`-копия файла + +### Known Issues +- **Лог Desktop** — кольцевой буфер ~12 строк. MCP-ошибки agentmemory/TestSprite (`prompts/list`) забивают его за ~39 секунд, вытесняя логи плагина. Не наша проблема. +- **Worker threads** — SQLite синхронный (`node:sqlite` DatabaseSync), но с единым handle + batch уже быстро. Если понадобится — вынести в `worker_threads`. + +### Removed Upstream Features +- `generateCommitMessage` (commit.ts) — LLM для commit message не используется. Сообщение = `Sync opencode config (YYYY-MM-DD)` +- `opencode.db` исключён из sync plan (предотвращает затирание сессий при pull с другой машины). Сессии → per-session JSON merge (`syncSessions`) + +### Deploy +1. `npx tsc -p tsconfig.build.json && npx copyfiles -u 1 "src/command/**/*" dist/` +2. `robocopy dist "C:\Users\Aleks\.config\opencode\plugins\opencode-synced\dist" /MIR /NFL /NDL /NJH /NJS /NC /NS /NP` 3. Restart OpenCode Desktop -4. Test `/sync-pull`, `/sync-push`, `/sync-status` -5. Test session creation + push/pull between machines diff --git a/package.json b/package.json index 3ff5b34..f17fd43 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,7 @@ "publishConfig": { "access": "public" }, - "files": [ - "dist" - ], + "files": ["dist"], "devDependencies": { "@biomejs/biome": "2.3.10", "@commitlint/cli": "^20.2.0", @@ -49,8 +47,6 @@ "prepare": "husky" }, "lint-staged": { - "*.{js,ts,json}": [ - "biome check --write --no-errors-on-unmatched" - ] + "*.{js,ts,json}": ["biome check --write --no-errors-on-unmatched"] } } diff --git a/src/command/sync-enable-secrets.md b/src/command/sync-enable-secrets.md index 2a8b228..fb7225c 100644 --- a/src/command/sync-enable-secrets.md +++ b/src/command/sync-enable-secrets.md @@ -1,7 +1,14 @@ --- -description: Enable secrets sync (private repo required) +description: Enable secrets sync / Включить синхронизацию секретов (требуется приватный репозиторий) --- -Use the opencode_sync tool with command "enable-secrets". -If the user supplies extra secret paths, pass them via extraSecretPaths. -If they want MCP secrets committed in a private repo, pass includeMcpSecrets: true. +You MUST call the `opencode_sync` tool with `command="enable-secrets"`. +Do not answer with plain text only. + +Argument handling: +- If the user supplies extra secret paths, pass them via `extraSecretPaths`. +- If they want MCP secrets committed in a private repo, pass `includeMcpSecrets: true`. + +Reminder: +- Enabling secrets requires the sync repo to be private — the tool will enforce this. +- Secrets are synced via a configured backend (e.g., 1Password), not stored in git directly. diff --git a/src/command/sync-init.md b/src/command/sync-init.md index 50ebe35..3fb46d2 100644 --- a/src/command/sync-init.md +++ b/src/command/sync-init.md @@ -1,5 +1,5 @@ --- -description: Initialize opencode-synced configuration +description: Initialize opencode-synced configuration / Инициализировать конфигурацию синхронизации --- You MUST call the `opencode_sync` tool with `command="init"`. diff --git a/src/command/sync-link.md b/src/command/sync-link.md index 9c64e64..b66ea2b 100644 --- a/src/command/sync-link.md +++ b/src/command/sync-link.md @@ -1,5 +1,5 @@ --- -description: Link this computer to an existing sync repo +description: Link to an existing sync repo / Привязать компьютер к существующему репозиторию синхронизации --- You MUST call the `opencode_sync` tool with `command="link"`. diff --git a/src/command/sync-pull.md b/src/command/sync-pull.md index 66a6a5f..6491980 100644 --- a/src/command/sync-pull.md +++ b/src/command/sync-pull.md @@ -1,6 +1,10 @@ --- -description: Pull and apply synced opencode config +description: Pull and apply synced config / Загрузить и применить синхронизированную конфигурацию --- -Use the opencode_sync tool with command "pull". -If updates are applied, remind the user to restart opencode. +You MUST call the `opencode_sync` tool with `command="pull"`. +Do not answer with plain text only. + +Reminder: +- Pull applies remote config to local — after a successful pull, tell the user to restart opencode. +- If the local repo has uncommitted changes, pull will fail — suggest /sync-resolve first. diff --git a/src/command/sync-push.md b/src/command/sync-push.md index cdc5a12..89f9172 100644 --- a/src/command/sync-push.md +++ b/src/command/sync-push.md @@ -1,5 +1,10 @@ +--- +description: Push local config / Отправить локальную конфигурацию --- -description: Push local opencode config to the sync repo ---- -Use the opencode_sync tool with command "push". +You MUST call the `opencode_sync` tool with `command="push"`. +Do not answer with plain text only. + +Reminder: +- The tool will auto-commit and push local changes to the sync repo. +- If there are uncommitted changes in the local repo, the push may fail — suggest /sync-resolve. diff --git a/src/command/sync-resolve.md b/src/command/sync-resolve.md index 0f8470a..6ead2a0 100644 --- a/src/command/sync-resolve.md +++ b/src/command/sync-resolve.md @@ -1,5 +1,11 @@ --- -description: Resolve uncommitted changes in sync repo +description: Resolve uncommitted changes / Разрешить незакоммиченные изменения в репозитории --- -Use the opencode_sync tool with command "resolve" to automatically resolve uncommitted changes in the local sync repository using AI. +You MUST call the `opencode_sync` tool with `command="resolve"`. +Do not answer with plain text only. + +Behavior: +- The tool analyzes uncommitted changes using AI and decides whether to commit or discard them. +- After resolution, the user can retry the failed command. +- If AI analysis is not available, the tool falls back to manual resolution instructions. diff --git a/src/command/sync-secrets-pull.md b/src/command/sync-secrets-pull.md index a3bcb1b..fa927e1 100644 --- a/src/command/sync-secrets-pull.md +++ b/src/command/sync-secrets-pull.md @@ -1,5 +1,10 @@ --- -description: Pull secrets from the configured backend +description: Pull secrets from backend / Загрузить секреты из настроенного бэкенда --- -Use the opencode_sync tool with command "secrets-pull". +You MUST call the `opencode_sync` tool with `command="secrets-pull"`. +Do not answer with plain text only. + +Behavior: +- Pulls secrets from the configured backend (e.g., 1Password) and applies them locally. +- Requires a secrets backend to be configured. diff --git a/src/command/sync-secrets-push.md b/src/command/sync-secrets-push.md index 4b4e834..055d85a 100644 --- a/src/command/sync-secrets-push.md +++ b/src/command/sync-secrets-push.md @@ -1,5 +1,10 @@ --- -description: Push secrets to the configured backend +description: Push secrets to backend / Отправить секреты в настроенный бэкенд --- -Use the opencode_sync tool with command "secrets-push". +You MUST call the `opencode_sync` tool with `command="secrets-push"`. +Do not answer with plain text only. + +Behavior: +- Pushes secrets to the configured backend (e.g., 1Password). +- Requires a secrets backend to be configured. diff --git a/src/command/sync-secrets-status.md b/src/command/sync-secrets-status.md index 67c15ad..59b8c77 100644 --- a/src/command/sync-secrets-status.md +++ b/src/command/sync-secrets-status.md @@ -1,5 +1,10 @@ --- -description: Show secrets backend status +description: Show secrets backend status / Показать статус бэкенда секретов --- -Use the opencode_sync tool with command "secrets-status". +You MUST call the `opencode_sync` tool with `command="secrets-status"`. +Do not answer with plain text only. + +Behavior: +- Shows the current status of the secrets backend (e.g., last sync, backend type). +- Requires a secrets backend to be configured. diff --git a/src/command/sync-sessions-backend.md b/src/command/sync-sessions-backend.md index 0f2ab6f..9307012 100644 --- a/src/command/sync-sessions-backend.md +++ b/src/command/sync-sessions-backend.md @@ -1,8 +1,9 @@ --- -description: Switch session sync backend between git and turso +description: Switch session sync backend (git/turso) / Переключить бэкенд синхронизации сессий --- You MUST call the `opencode_sync` tool with `command="sessions-backend"`. +Do not answer with plain text only. Argument handling: - `$ARGUMENTS` must be either `git` or `turso`. diff --git a/src/command/sync-sessions-cleanup-git.md b/src/command/sync-sessions-cleanup-git.md index f051872..4d93b98 100644 --- a/src/command/sync-sessions-cleanup-git.md +++ b/src/command/sync-sessions-cleanup-git.md @@ -1,7 +1,10 @@ --- -description: Remove deprecated git session artifacts after Turso migration +description: Remove deprecated git session artifacts / Удалить устаревшие git-артефакты сессий --- You MUST call the `opencode_sync` tool with `command="sessions-cleanup-git"`. +Do not answer with plain text only. -Use this only after sessions are running on Turso and the user confirms fallback artifacts are no longer needed. +Reminder: +- Use this only after sessions are running on Turso and the user confirms fallback artifacts are no longer needed. +- This removes data/sessions/*.json and data/opencode.db from the sync repo. diff --git a/src/command/sync-sessions-migrate-turso.md b/src/command/sync-sessions-migrate-turso.md index 6867060..f756d2c 100644 --- a/src/command/sync-sessions-migrate-turso.md +++ b/src/command/sync-sessions-migrate-turso.md @@ -1,8 +1,9 @@ --- -description: Migrate session sync from git artifacts to Turso backend +description: Migrate session sync to Turso / Перенести синхронизацию сессий из git в Turso --- You MUST call the `opencode_sync` tool with `command="sessions-migrate-turso"`. +Do not answer with plain text only. Behavior: - Ensure Turso setup is complete. diff --git a/src/command/sync-sessions-setup-turso.md b/src/command/sync-sessions-setup-turso.md index fd43664..0bd0602 100644 --- a/src/command/sync-sessions-setup-turso.md +++ b/src/command/sync-sessions-setup-turso.md @@ -1,8 +1,9 @@ --- -description: Install/auth/provision Turso for session sync on this machine +description: Setup Turso for session sync / Настроить Turso для синхронизации сессий --- You MUST call the `opencode_sync` tool with `command="sessions-setup-turso"`. +Do not answer with plain text only. Behavior: - Run Turso CLI install if missing. diff --git a/src/command/sync-status.md b/src/command/sync-status.md index 3a53e12..048bdd8 100644 --- a/src/command/sync-status.md +++ b/src/command/sync-status.md @@ -1,5 +1,10 @@ --- -description: Show opencode-synced status +description: Show sync status / Показать статус синхронизации --- -Use the opencode_sync tool with command "status" and report the results. +You MUST call the `opencode_sync` tool with `command="status"`. +Do not answer with plain text only. + +Reminder: +- Report the results to the user in a readable format (repo, branch, last push/pull, working tree state). +- If sessions sync is enabled, mention the session backend (git or turso) and last sync times. diff --git a/src/index.ts b/src/index.ts index 04fd7a5..ecce81d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,11 +23,17 @@ interface ParsedCommand { } function parseFrontmatter(content: string): { frontmatter: CommandFrontmatter; body: string } { + // EN: Strip UTF-8 BOM, normalize CRLF → LF (cross-platform .md files) + // RU: Удаление BOM, нормализация CRLF → LF (кроссплатформенность) + const normalized = content.replace(/^\uFEFF/, '').replace(/\r\n/g, '\n'); const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/; - const match = content.match(frontmatterRegex); + const match = normalized.match(frontmatterRegex); if (!match) { - return { frontmatter: {}, body: content.trim() }; + // EN: No frontmatter — use first non-empty line as description fallback + // RU: Нет frontmatter — первая непустая строка как описание + const firstLine = normalized.split('\n').find((l) => l.trim()) ?? ''; + return { frontmatter: { description: firstLine.trim() }, body: normalized.trim() }; } const [, yamlContent, body] = match; @@ -103,7 +109,9 @@ async function loadCommands(): Promise { frontmatter, template: body, }); - } catch {} + } catch { + // Skip malformed command files + } } return commands; @@ -270,7 +278,9 @@ export const opencodeConfigSync: Plugin = async (ctx) => { // Delay startup sync slightly to ensure TUI is connected setTimeout(() => { - void service.startupSync(); + service.startupSync().catch(() => { + // Errors are already logged internally by startupSync + }); }, 1000); return { diff --git a/src/sync/apply.ts b/src/sync/apply.ts index 90b75b4..4c798dd 100644 --- a/src/sync/apply.ts +++ b/src/sync/apply.ts @@ -1,3 +1,4 @@ +import type { Dirent } from 'node:fs'; import { promises as fs } from 'node:fs'; import path from 'node:path'; @@ -110,6 +111,90 @@ export async function syncLocalToRepo( await writeExtraPathManifest(plan, plan.extraSecrets); } +interface FileEntry { + relativePath: string; + mtimeMs: number; + size: number; + isDirectory: boolean; +} + +// EN: Recursively walk a directory, collecting file entries with mtime + size for diff comparison +// RU: Рекурсивный обход директории с mtime + size для diff-сравнения +async function walkDir(rootPath: string, relativeDir = ''): Promise { + const entries: FileEntry[] = []; + let dirEntries: Dirent[]; + try { + dirEntries = await fs.readdir(rootPath, { withFileTypes: true }); + } catch { + return entries; + } + for (const entry of dirEntries) { + const relativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name; + if (entry.isDirectory()) { + entries.push({ relativePath, mtimeMs: 0, size: 0, isDirectory: true }); + const nested = await walkDir(path.join(rootPath, entry.name), relativePath); + entries.push(...nested); + } else if (entry.isFile()) { + try { + const stat = await fs.stat(path.join(rootPath, entry.name)); + entries.push({ relativePath, mtimeMs: stat.mtimeMs, size: stat.size, isDirectory: false }); + } catch { + // skip unreadable files + } + } + } + return entries; +} + +// EN: Diff-based directory copy — only copies new/changed files, removes deleted ones +// EN: Avoids full directory recreation (unlike removePath + copyDirRecursive) +// RU: Diff-based копирование — копирует только новые/изменённые файлы, удаляет пропавшие +// RU: Без полного пересоздания директорий (в отличие от removePath + copyDirRecursive) +async function applyDirDiff(sourceRoot: string, destRoot: string): Promise { + const [sourceFiles, destFiles] = await Promise.all([walkDir(sourceRoot), walkDir(destRoot)]); + + const sourceMap = new Map(); + for (const f of sourceFiles) sourceMap.set(f.relativePath, f); + + const destMap = new Map(); + for (const f of destFiles) destMap.set(f.relativePath, f); + + const actions: Array<() => Promise> = []; + + for (const [relPath, src] of sourceMap) { + const dst = destMap.get(relPath); + if (!dst) { + if (src.isDirectory) { + actions.push(async () => { + await fs.mkdir(path.join(destRoot, relPath), { recursive: true }); + }); + } else { + actions.push(async () => { + await copyFileWithMode(path.join(sourceRoot, relPath), path.join(destRoot, relPath)); + }); + } + } else if ( + !src.isDirectory && + !dst.isDirectory && + (src.mtimeMs !== dst.mtimeMs || src.size !== dst.size) + ) { + actions.push(async () => { + await copyFileWithMode(path.join(sourceRoot, relPath), path.join(destRoot, relPath)); + }); + } + } + + for (const [relPath] of destMap) { + if (!sourceMap.has(relPath)) { + actions.push(async () => { + await removePath(path.join(destRoot, relPath)); + }); + } + } + + await Promise.all(actions.map((a) => a())); +} + async function copyItem( sourcePath: string, destinationPath: string, @@ -137,8 +222,8 @@ async function copyItem( return; } - await removePath(destinationPath); - await copyDirRecursive(sourcePath, destinationPath); + await fs.mkdir(destinationPath, { recursive: true }); + await applyDirDiff(sourcePath, destinationPath); } async function copyConfigForRepo( diff --git a/src/sync/commit.ts b/src/sync/commit.ts index 20df918..9638377 100644 --- a/src/sync/commit.ts +++ b/src/sync/commit.ts @@ -58,7 +58,9 @@ export async function generateCommitMessage( if (sessionId) { try { await ctx.client.session.delete({ path: { id: sessionId } }); - } catch {} + } catch { + // Session deletion is best-effort cleanup + } } } } diff --git a/src/sync/config.ts b/src/sync/config.ts index 9a656f6..72728be 100644 --- a/src/sync/config.ts +++ b/src/sync/config.ts @@ -89,6 +89,7 @@ export interface SyncState { lastSecretsHash?: string; lastSessionPull?: string; lastSessionPush?: string; + lastHead?: string; } export async function pathExists(filePath: string): Promise { diff --git a/src/sync/paths.test.ts b/src/sync/paths.test.ts index 25e9621..a3f04f3 100644 --- a/src/sync/paths.test.ts +++ b/src/sync/paths.test.ts @@ -233,7 +233,7 @@ describe('buildSyncPlan', () => { expect(plan.extraConfigs.allowlist.length).toBe(1); }); - it('includes sqlite and legacy session paths when includeSessions is true', () => { + it('excludes session paths from plan when sessions use syncSessions merge', () => { const env = { HOME: '/home/test' } as NodeJS.ProcessEnv; const locations = resolveSyncLocations(env, 'linux'); const config: SyncConfig = { @@ -243,20 +243,10 @@ describe('buildSyncPlan', () => { }; const plan = buildSyncPlan(normalizeSyncConfig(config), locations, '/repo', 'linux'); - const expectedSessionPaths = [ - '/.local/share/opencode/opencode.db', - '/.local/share/opencode/storage/session', - '/.local/share/opencode/storage/message', - '/.local/share/opencode/storage/part', - '/.local/share/opencode/storage/session_diff', - ]; - - for (const suffix of expectedSessionPaths) { - const sessionItem = plan.items.find((item) => item.localPath.endsWith(suffix)); - expect(sessionItem).toBeTruthy(); - expect(sessionItem?.isSecret).toBe(true); - expect(sessionItem?.preserveWhenMissing).toBe(true); - } + const sessionDbItem = plan.items.find((item) => item.localPath.endsWith('opencode.db')); + expect(sessionDbItem).toBeUndefined(); + const sessionDirItem = plan.items.find((item) => item.localPath.includes('storage/session')); + expect(sessionDirItem).toBeUndefined(); }); it('excludes git session paths when using turso session backend', () => { diff --git a/src/sync/paths.ts b/src/sync/paths.ts index c7605aa..2640eba 100644 --- a/src/sync/paths.ts +++ b/src/sync/paths.ts @@ -52,15 +52,20 @@ const DEFAULT_SYNC_CONFIG_NAME = 'opencode-synced.jsonc'; const DEFAULT_OVERRIDES_NAME = 'opencode-synced.overrides.jsonc'; const DEFAULT_STATE_NAME = 'sync-state.json'; -const CONFIG_DIRS = ['agent', 'command', 'mode', 'tool', 'themes', 'plugin']; -const SESSION_DIRS = ['storage/session', 'storage/message', 'storage/part', 'storage/session_diff']; -const SESSION_DB_FILE = 'opencode.db'; +const CONFIG_DIRS = ['agent', 'command', 'commands', 'mode', 'tool', 'themes', 'plugin', 'plugins']; const PROMPT_STASH_FILES = ['prompt-stash.jsonl', 'prompt-history.jsonl']; const MODEL_FAVORITES_FILE = 'model.json'; const SKILLS_DIR = 'skills'; const HOME_AGENTS_DIR = '.agents'; const GLOBAL_DAT_FILE = 'opencode.global.dat'; +// EN: Platform-aware path.join — when testing (platform != runtime), uses posix/win32 module +// RU: path.join с учётом платформы — при тестах (platform != runtime) использует posix/win32 модуль +function platformJoin(platform: NodeJS.Platform, ...parts: string[]): string { + if (platform === 'win32') return path.win32.join(...parts); + return path.posix.join(...parts); +} + export function resolveHomeDir( env: NodeJS.ProcessEnv = process.env, platform: NodeJS.Platform = process.platform @@ -87,9 +92,9 @@ export function resolveXdgPaths( }; } - const configDir = env.XDG_CONFIG_HOME ?? path.join(homeDir, '.config'); - const dataDir = env.XDG_DATA_HOME ?? path.join(homeDir, '.local', 'share'); - const stateDir = env.XDG_STATE_HOME ?? path.join(homeDir, '.local', 'state'); + const configDir = env.XDG_CONFIG_HOME ?? platformJoin(platform, homeDir, '.config'); + const dataDir = env.XDG_DATA_HOME ?? platformJoin(platform, homeDir, '.local', 'share'); + const stateDir = env.XDG_STATE_HOME ?? platformJoin(platform, homeDir, '.local', 'state'); return { homeDir, configDir, dataDir, stateDir }; } @@ -101,17 +106,17 @@ export function resolveSyncLocations( const xdg = resolveXdgPaths(env, platform); const customConfigDir = env.OPENCODE_CONFIG_DIR; const configRoot = customConfigDir - ? path.resolve(expandHome(customConfigDir, xdg.homeDir)) - : path.join(xdg.configDir, 'opencode'); - const dataRoot = path.join(xdg.dataDir, 'opencode'); + ? platformJoin(platform, expandHome(customConfigDir, xdg.homeDir)) + : platformJoin(platform, xdg.configDir, 'opencode'); + const dataRoot = platformJoin(platform, xdg.dataDir, 'opencode'); return { xdg, configRoot, - syncConfigPath: path.join(configRoot, DEFAULT_SYNC_CONFIG_NAME), - overridesPath: path.join(configRoot, DEFAULT_OVERRIDES_NAME), - statePath: path.join(dataRoot, DEFAULT_STATE_NAME), - defaultRepoDir: path.join(dataRoot, 'opencode-synced', 'repo'), + syncConfigPath: platformJoin(platform, configRoot, DEFAULT_SYNC_CONFIG_NAME), + overridesPath: platformJoin(platform, configRoot, DEFAULT_OVERRIDES_NAME), + statePath: platformJoin(platform, dataRoot, DEFAULT_STATE_NAME), + defaultRepoDir: platformJoin(platform, dataRoot, 'opencode-synced', 'repo'), }; } @@ -119,7 +124,7 @@ export function expandHome(inputPath: string, homeDir: string): string { if (!inputPath) return inputPath; if (!homeDir) return inputPath; if (inputPath === '~') return homeDir; - if (inputPath.startsWith('~/')) return path.join(homeDir, inputPath.slice(2)); + if (inputPath.startsWith('~/')) return `${homeDir}/${inputPath.slice(2)}`; return inputPath; } @@ -128,8 +133,9 @@ export function normalizePath( homeDir: string, platform: NodeJS.Platform = process.platform ): string { - const expanded = expandHome(inputPath, homeDir); - const resolved = path.resolve(expanded); + const pj = (...parts: string[]) => platformJoin(platform, ...parts); + const expanded = expandHome(inputPath, homeDir).replace(/\\/g, '/'); + const resolved = pj(expanded); if (platform === 'win32') { return resolved.toLowerCase(); } @@ -167,12 +173,13 @@ export function resolveProjectsFilePath( env: NodeJS.ProcessEnv = process.env, platform: NodeJS.Platform = process.platform ): string { + const pj = (...parts: string[]) => platformJoin(platform, ...parts); if (platform === 'win32') { - const appData = env.APPDATA ?? path.join(env.USERPROFILE ?? '', 'AppData', 'Roaming'); - return path.join(appData, 'ai.opencode.desktop', GLOBAL_DAT_FILE); + const appData = env.APPDATA ?? pj(env.USERPROFILE ?? '', 'AppData', 'Roaming'); + return pj(appData, 'ai.opencode.desktop', GLOBAL_DAT_FILE); } - const dataDir = env.XDG_DATA_HOME ?? path.join(resolveHomeDir(env, platform), '.local', 'share'); - return path.join(dataDir, 'opencode', GLOBAL_DAT_FILE); + const dataDir = env.XDG_DATA_HOME ?? pj(resolveHomeDir(env, platform), '.local', 'share'); + return pj(dataDir, 'opencode', GLOBAL_DAT_FILE); } export function buildSyncPlan( @@ -181,27 +188,28 @@ export function buildSyncPlan( repoRoot: string, platform: NodeJS.Platform = process.platform ): SyncPlan { + const pj = (...parts: string[]) => platformJoin(platform, ...parts); const configRoot = locations.configRoot; - const dataRoot = path.join(locations.xdg.dataDir, 'opencode'); - const stateRoot = path.join(locations.xdg.stateDir, 'opencode'); - const repoConfigRoot = path.join(repoRoot, 'config'); - const repoDataRoot = path.join(repoRoot, 'data'); - const repoSecretsRoot = path.join(repoRoot, 'secrets'); - const repoStateRoot = path.join(repoRoot, 'state'); - const repoExtraDir = path.join(repoSecretsRoot, 'extra'); - const manifestPath = path.join(repoSecretsRoot, 'extra-manifest.json'); - const repoConfigExtraDir = path.join(repoConfigRoot, 'extra'); - const configManifestPath = path.join(repoConfigRoot, 'extra-manifest.json'); + const dataRoot = pj(locations.xdg.dataDir, 'opencode'); + const stateRoot = pj(locations.xdg.stateDir, 'opencode'); + const repoConfigRoot = pj(repoRoot, 'config'); + const repoDataRoot = pj(repoRoot, 'data'); + const repoSecretsRoot = pj(repoRoot, 'secrets'); + const repoStateRoot = pj(repoRoot, 'state'); + const repoExtraDir = pj(repoSecretsRoot, 'extra'); + const manifestPath = pj(repoSecretsRoot, 'extra-manifest.json'); + const repoConfigExtraDir = pj(repoConfigRoot, 'extra'); + const configManifestPath = pj(repoConfigRoot, 'extra-manifest.json'); const items: SyncItem[] = []; const usingSecretsBackend = hasSecretsBackend(config); - const authJsonPath = path.join(dataRoot, 'auth.json'); - const mcpAuthJsonPath = path.join(dataRoot, 'mcp-auth.json'); + const authJsonPath = pj(dataRoot, 'auth.json'); + const mcpAuthJsonPath = pj(dataRoot, 'mcp-auth.json'); const addFile = (name: string, isSecret: boolean, isConfigFile: boolean): void => { items.push({ - localPath: path.join(configRoot, name), - repoPath: path.join(repoConfigRoot, name), + localPath: pj(configRoot, name), + repoPath: pj(repoConfigRoot, name), type: 'file', isSecret, isConfigFile, @@ -215,8 +223,8 @@ export function buildSyncPlan( for (const dirName of CONFIG_DIRS) { items.push({ - localPath: path.join(configRoot, dirName), - repoPath: path.join(repoConfigRoot, dirName), + localPath: pj(configRoot, dirName), + repoPath: pj(repoConfigRoot, dirName), type: 'dir', isSecret: false, isConfigFile: false, @@ -225,8 +233,8 @@ export function buildSyncPlan( if (config.includeOpencodeSkills !== false) { items.push({ - localPath: path.join(configRoot, SKILLS_DIR), - repoPath: path.join(repoConfigRoot, SKILLS_DIR), + localPath: pj(configRoot, SKILLS_DIR), + repoPath: pj(repoConfigRoot, SKILLS_DIR), type: 'dir', isSecret: false, isConfigFile: false, @@ -235,8 +243,8 @@ export function buildSyncPlan( if (config.includeAgentsDir !== false) { items.push({ - localPath: path.join(locations.xdg.homeDir, HOME_AGENTS_DIR), - repoPath: path.join(repoConfigRoot, HOME_AGENTS_DIR), + localPath: pj(locations.xdg.homeDir, HOME_AGENTS_DIR), + repoPath: pj(repoConfigRoot, HOME_AGENTS_DIR), type: 'dir', isSecret: false, isConfigFile: false, @@ -245,8 +253,8 @@ export function buildSyncPlan( if (config.includeModelFavorites !== false) { items.push({ - localPath: path.join(stateRoot, MODEL_FAVORITES_FILE), - repoPath: path.join(repoStateRoot, MODEL_FAVORITES_FILE), + localPath: pj(stateRoot, MODEL_FAVORITES_FILE), + repoPath: pj(repoStateRoot, MODEL_FAVORITES_FILE), type: 'file', isSecret: false, isConfigFile: false, @@ -265,7 +273,7 @@ export function buildSyncPlan( }, { localPath: mcpAuthJsonPath, - repoPath: path.join(repoDataRoot, 'mcp-auth.json'), + repoPath: pj(repoDataRoot, 'mcp-auth.json'), type: 'file', isSecret: true, isConfigFile: false, @@ -281,8 +289,8 @@ export function buildSyncPlan( if (config.includePromptStash) { for (const fileName of PROMPT_STASH_FILES) { items.push({ - localPath: path.join(stateRoot, fileName), - repoPath: path.join(repoStateRoot, fileName), + localPath: pj(stateRoot, fileName), + repoPath: pj(repoStateRoot, fileName), type: 'file', isSecret: true, isConfigFile: false, @@ -338,13 +346,14 @@ function buildExtraPathPlan( manifestPath: string, platform: NodeJS.Platform ): ExtraPathPlan { + const pj = (...parts: string[]) => platformJoin(platform, ...parts); const allowlist = (inputPaths ?? []).map((entry) => normalizePath(entry, locations.xdg.homeDir, platform) ); const entries = allowlist.map((sourcePath) => ({ sourcePath, - repoPath: path.join(repoExtraDir, encodeExtraPath(sourcePath)), + repoPath: pj(repoExtraDir, encodeExtraPath(sourcePath)), })); return { diff --git a/src/sync/repo.ts b/src/sync/repo.ts index c5ffa9b..235dce6 100644 --- a/src/sync/repo.ts +++ b/src/sync/repo.ts @@ -161,6 +161,15 @@ export async function pushBranch($: Shell, repoDir: string, branch: string): Pro } } +export async function getHeadHash($: Shell, repoDir: string): Promise { + try { + const output = await $`git -C ${repoDir} rev-parse HEAD`.quiet().text(); + return output.trim() || null; + } catch { + return null; + } +} + async function getCurrentBranch($: Shell, repoDir: string): Promise { try { const output = await $`git -C ${repoDir} rev-parse --abbrev-ref HEAD`.quiet().text(); diff --git a/src/sync/service.ts b/src/sync/service.ts index f63c513..156b54b 100644 --- a/src/sync/service.ts +++ b/src/sync/service.ts @@ -3,7 +3,7 @@ import path from 'node:path'; import type { PluginInput } from '@opencode-ai/plugin'; import { syncLocalToRepo, syncRepoToLocal } from './apply.js'; -import { generateCommitMessage } from './commit.js'; + import type { NormalizedSyncConfig } from './config.js'; import { canCommitMcpSecrets, @@ -19,7 +19,13 @@ import { import { SyncCommandError, SyncConfigMissingError } from './errors.js'; import type { SyncLockInfo } from './lock.js'; import { withSyncLock } from './lock.js'; -import { buildSyncPlan, resolveProjectsFilePath, resolveRepoRoot, resolveSyncLocations } from './paths.js'; +import { + buildSyncPlan, + resolveProjectsFilePath, + resolveRepoRoot, + resolveSyncLocations, +} from './paths.js'; +import { syncGlobalData } from './projects-merge.js'; import { commitAll, ensureRepoCloned, @@ -27,6 +33,7 @@ import { fetchAndFastForward, findSyncRepo, getAuthenticatedUser, + getHeadHash, getRepoStatus, hasLocalChanges, isRepoCloned, @@ -43,14 +50,13 @@ import { resolveSecretsBackendConfig, type SecretsBackend, } from './secrets-backend.js'; +import { syncSessions } from './session-merge.js'; +import { createNodeShell } from './shell.js'; import { createTursoSessionBackend, isRetryableTursoError, type TursoSyncPreference, } from './turso.js'; -import { syncSessions } from './session-merge.js'; -import { syncGlobalData } from './projects-merge.js'; -import { createNodeShell } from './shell.js'; import { createLogger, extractTextFromResponse, @@ -483,8 +489,10 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } tursoIdleFlushTimer = setTimeout(() => { tursoIdleFlushTimer = null; - void skipIfBusy(async () => { + skipIfBusy(async () => { await flushQueuedTursoSync('idle-event'); + }).catch(() => { + // Errors are already logged internally by skipIfBusy }); }, 250); }; @@ -535,7 +543,7 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { stopTursoSyncLoop(); tursoSyncIntervalSec = nextInterval; tursoSyncTimer = setInterval(() => { - void skipIfBusy(async () => { + skipIfBusy(async () => { const latest = await loadSyncConfig(locations); if (!latest || !isTursoSessionBackend(latest)) { stopTursoSyncLoop(); @@ -550,6 +558,8 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { if (result.warning) { log.warn(result.warning, { reason: 'background' }); } + }).catch(() => { + // Errors are already logged internally by skipIfBusy }); }, nextInterval * 1000); }; @@ -808,7 +818,12 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } ensureTursoSyncLoop(config); - await notify(ctx.client, '🚀', `Sync configured — ${repoIdentifier} (${resolveRepoBranch(config)})`, 'success'); + await notify( + ctx.client, + '🚀', + `Sync configured — ${repoIdentifier} (${resolveRepoBranch(config)})`, + 'success' + ); return lines.join('\n'); }), link: (options: LinkOptions) => @@ -919,7 +934,12 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { lines.push('', ...linkNotes); } - await notify(ctx.client, '🔗', `Linked to ${found.owner}/${found.name}. Restart opencode to apply.`, 'success'); + await notify( + ctx.client, + '🔗', + `Linked to ${found.owner}/${found.name}. Restart opencode to apply.`, + 'success' + ); return lines.join('\n'); }), pull: () => @@ -965,7 +985,10 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } if (config.includeProjects) { - syncGlobalData(resolveProjectsFilePath(), path.join(repoRoot, 'data', 'opencode.global.dat')); + syncGlobalData( + resolveProjectsFilePath(), + path.join(repoRoot, 'data', 'opencode.global.dat') + ); } await updateState(locations, { @@ -1014,7 +1037,10 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } if (config.includeProjects) { - syncGlobalData(resolveProjectsFilePath(), path.join(repoRoot, 'data', 'opencode.global.dat')); + syncGlobalData( + resolveProjectsFilePath(), + path.join(repoRoot, 'data', 'opencode.global.dat') + ); } await syncLocalToRepo(plan, overrides, { @@ -1051,7 +1077,8 @@ export function createSyncService(ctx: SyncServiceContext): SyncService { } } - const message = await generateCommitMessage({ client: ctx.client, $: ctx.$ }, repoRoot); + const now = new Date(); + const message = `Sync opencode config (${now.toISOString().slice(0, 10)})`; await commitAll(ctx.$, repoRoot, message); await pushBranch(ctx.$, repoRoot, branch); @@ -1382,6 +1409,10 @@ function toRepoRelativePath(repoRoot: string, absolutePath: string): string { return path.relative(repoRoot, absolutePath).split(path.sep).join('/'); } +// EN: Startup sync — auto-commit dirty, HEAD-cache fetch skip, pull + session/project merge +// EN: github push is fire-and-forget (.catch) — doesn't block startup +// RU: Стартовая синхронизация — auto-commit при dirty, пропуск fetch по HEAD-кэшу, pull + merge сессий/проектов +// RU: push в GitHub — fire-and-forget (.catch) — не блокирует запуск async function runStartup( ctx: SyncServiceContext, locations: ReturnType, @@ -1401,42 +1432,70 @@ async function runStartup( const branch = await resolveBranch(ctx, config, repoRoot); log.debug('Resolved branch', { branch }); + const head = await getHeadHash(ctx.$, repoRoot); + const state = await loadState(locations); const dirty = await hasLocalChanges(ctx.$, repoRoot); if (dirty) { - log.warn('Uncommitted changes detected', { repoRoot }); - await showToast( - ctx.client, - `Uncommitted changes detected. Run /sync-resolve to auto-fix, or manually resolve in: ${repoRoot}`, - 'warning' - ); - return; + log.warn('Uncommitted changes detected, attempting auto-commit', { repoRoot }); + try { + const date = new Date().toISOString().slice(0, 10).replace(/-/g, '.'); + await commitAll(ctx.$, repoRoot, `Sync opencode config (${date})`); + const branch = await resolveBranch(ctx, config, repoRoot); + const commitHead = await getHeadHash(ctx.$, repoRoot); + // EN: Background push — don't block startup waiting for GitHub + // RU: Фоновый push — не ждём GitHub, не блокируем стартап + pushBranch(ctx.$, repoRoot, branch).catch((err) => + log.warn('Background push failed', { error: formatError(err) }) + ); + await updateState(locations, { + lastPush: new Date().toISOString(), + lastHead: commitHead ?? undefined, + }); + log.info('Auto-committed and pushed pending changes'); + await showToast(ctx.client, 'Pending changes committed and pushed', 'info'); + } catch (error) { + log.warn('Could not auto-commit pending changes', { error: formatError(error) }); + await showToast( + ctx.client, + `Uncommitted changes detected. Run /sync-resolve to auto-fix, or manually resolve in: ${repoRoot}`, + 'warning' + ); + return; + } } - const update = await fetchAndFastForward(ctx.$, repoRoot, branch); - if (update.updated) { - log.info('Pulled remote changes', { branch }); - const overrides = await loadOverrides(locations); - const plan = buildSyncPlan(config, locations, repoRoot); - await syncRepoToLocal(plan, overrides); - await options.runSecretsPullIfConfigured(config); - - if (config.includeSessions && !isTursoSessionBackend(config)) { - const sessionDbPath = path.join(locations.xdg.dataDir, 'opencode', 'opencode.db'); - const sessionsDir = path.join(repoRoot, 'data', 'sessions'); - const result = await syncSessions(sessionDbPath, sessionsDir); - log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); - } + const shouldFetch = !head || head !== state.lastHead || dirty; + if (shouldFetch) { + const update = await fetchAndFastForward(ctx.$, repoRoot, branch); + if (update.updated) { + log.info('Pulled remote changes', { branch }); + const overrides = await loadOverrides(locations); + const plan = buildSyncPlan(config, locations, repoRoot); + await syncRepoToLocal(plan, overrides); + await options.runSecretsPullIfConfigured(config); + + if (config.includeSessions && !isTursoSessionBackend(config)) { + const sessionDbPath = path.join(locations.xdg.dataDir, 'opencode', 'opencode.db'); + const sessionsDir = path.join(repoRoot, 'data', 'sessions'); + const result = await syncSessions(sessionDbPath, sessionsDir); + log.info(`Session sync result: ${result.total} total, ${result.merged} merged`); + } - if (config.includeProjects) { - syncGlobalData(resolveProjectsFilePath(), path.join(repoRoot, 'data', 'opencode.global.dat')); - } + if (config.includeProjects) { + syncGlobalData( + resolveProjectsFilePath(), + path.join(repoRoot, 'data', 'opencode.global.dat') + ); + } - await updateState(locations, { - lastPull: new Date().toISOString(), - lastRemoteUpdate: new Date().toISOString(), - }); - await showToast(ctx.client, 'Config updated. Restart opencode to apply.', 'info'); - return; + await updateState(locations, { + lastPull: new Date().toISOString(), + lastRemoteUpdate: new Date().toISOString(), + lastHead: head ?? undefined, + }); + await showToast(ctx.client, 'Config updated. Restart opencode to apply.', 'info'); + return; + } } const overrides = await loadOverrides(locations); @@ -1463,12 +1522,15 @@ async function runStartup( return; } - const message = await generateCommitMessage({ client: ctx.client, $: ctx.$ }, repoRoot); + const now = new Date(); + const message = `Sync opencode config (${now.toISOString().slice(0, 10)})`; log.info('Pushing local changes', { message }); await commitAll(ctx.$, repoRoot, message); await pushBranch(ctx.$, repoRoot, branch); + const newHead = await getHeadHash(ctx.$, repoRoot); await updateState(locations, { lastPush: new Date().toISOString(), + lastHead: newHead ?? undefined, }); } @@ -1669,11 +1731,21 @@ async function analyzeAndDecideResolution( if (sessionId) { try { await ctx.client.session.delete({ path: { id: sessionId } }); - } catch {} + } catch { + // Session deletion is best-effort cleanup + } } } } catch (error) { - console.error('[ERROR] AI resolution analysis failed:', error); + ctx.client.app + .log({ + body: { + service: 'opencode-synced', + level: 'error', + message: `AI resolution analysis failed: ${formatError(error)}`, + }, + }) + .catch(() => {}); return { action: 'manual', reason: `Error analyzing changes: ${formatError(error)}` }; } } diff --git a/src/sync/session-db.ts b/src/sync/session-db.ts index f1f8562..99c3d84 100644 --- a/src/sync/session-db.ts +++ b/src/sync/session-db.ts @@ -1,6 +1,6 @@ -import { DatabaseSync } from 'node:sqlite'; import fs from 'node:fs'; import path from 'node:path'; +import { DatabaseSync } from 'node:sqlite'; export interface SessionMeta { id: string; @@ -88,57 +88,217 @@ export interface Session { } const SESSION_COLUMNS = [ - 'id', 'project_id', 'parent_id', 'slug', 'directory', - 'title', 'version', 'share_url', 'summary_additions', - 'summary_deletions', 'summary_files', 'summary_diffs', - 'revert', 'permission', 'time_created', 'time_updated', - 'time_compacting', 'time_archived', 'workspace_id', - 'path', 'agent', 'model', 'cost', 'tokens_input', - 'tokens_output', 'tokens_reasoning', 'tokens_cache_read', + 'id', + 'project_id', + 'parent_id', + 'slug', + 'directory', + 'title', + 'version', + 'share_url', + 'summary_additions', + 'summary_deletions', + 'summary_files', + 'summary_diffs', + 'revert', + 'permission', + 'time_created', + 'time_updated', + 'time_compacting', + 'time_archived', + 'workspace_id', + 'path', + 'agent', + 'model', + 'cost', + 'tokens_input', + 'tokens_output', + 'tokens_reasoning', + 'tokens_cache_read', 'tokens_cache_write', ]; const MESSAGE_COLUMNS = ['id', 'session_id', 'time_created', 'time_updated', 'data']; const PART_COLUMNS = ['id', 'message_id', 'session_id', 'time_created', 'time_updated', 'data']; -const SESSION_MESSAGE_COLUMNS = ['id', 'session_id', 'type', 'time_created', 'time_updated', 'data']; -const TODO_COLUMNS = ['session_id', 'content', 'status', 'priority', 'position', 'time_created', 'time_updated']; +const SESSION_MESSAGE_COLUMNS = [ + 'id', + 'session_id', + 'type', + 'time_created', + 'time_updated', + 'data', +]; +const TODO_COLUMNS = [ + 'session_id', + 'content', + 'status', + 'priority', + 'position', + 'time_created', + 'time_updated', +]; const SHARE_COLUMNS = ['session_id', 'id', 'secret', 'url', 'time_created', 'time_updated']; function openDB(dbPath: string): DatabaseSync { return new DatabaseSync(dbPath); } +// EN: Database handle variants — used by syncSessions to open DB once +// RU: Варианты с проброшенным handle — syncSessions открывает БД один раз +// EN: Force WAL checkpoint so subsequent reads see all committed writes +// RU: Принудительный WAL checkpoint, чтобы последующие чтения видели все записанные данные +export function checkpointDB(db: DatabaseSync): void { + try { + db.exec('PRAGMA wal_checkpoint'); + } catch { + // checkpoint may fail if another connection has a write lock — ignore + } +} + function readSessionMeta(db: DatabaseSync, id: string): SessionMeta | null { - const row = db.prepare(`SELECT ${SESSION_COLUMNS.join(', ')} FROM session WHERE id = ?`).get(id) as Record | undefined; + const row = db + .prepare(`SELECT ${SESSION_COLUMNS.join(', ')} FROM session WHERE id = ?`) + .get(id) as Record | undefined; if (!row) return null; return row as unknown as SessionMeta; } function readMessages(db: DatabaseSync, sessionId: string): Message[] { - const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS.join(', ')} FROM message WHERE session_id = ?`).all(sessionId) as Record[]; + const rows = db + .prepare(`SELECT ${MESSAGE_COLUMNS.join(', ')} FROM message WHERE session_id = ?`) + .all(sessionId) as Record[]; return rows.map((row) => { const msg = row as unknown as Message; - const partRows = db.prepare(`SELECT ${PART_COLUMNS.join(', ')} FROM part WHERE message_id = ?`).all(msg.id) as Record[]; + const partRows = db + .prepare(`SELECT ${PART_COLUMNS.join(', ')} FROM part WHERE message_id = ?`) + .all(msg.id) as Record[]; msg.parts = partRows.map((pr) => pr as unknown as Part); return msg; }); } function readSessionMessages(db: DatabaseSync, sessionId: string): SessionMessage[] { - const rows = db.prepare(`SELECT ${SESSION_MESSAGE_COLUMNS.join(', ')} FROM session_message WHERE session_id = ?`).all(sessionId) as Record[]; + const rows = db + .prepare( + `SELECT ${SESSION_MESSAGE_COLUMNS.join(', ')} FROM session_message WHERE session_id = ?` + ) + .all(sessionId) as Record[]; return rows.map((r) => r as unknown as SessionMessage); } function readTodos(db: DatabaseSync, sessionId: string): Todo[] { - const rows = db.prepare(`SELECT ${TODO_COLUMNS.join(', ')} FROM todo WHERE session_id = ?`).all(sessionId) as Record[]; + const rows = db + .prepare(`SELECT ${TODO_COLUMNS.join(', ')} FROM todo WHERE session_id = ?`) + .all(sessionId) as Record[]; return rows.map((r) => r as unknown as Todo); } function readShares(db: DatabaseSync, sessionId: string): SessionShare[] { - const rows = db.prepare(`SELECT ${SHARE_COLUMNS.join(', ')} FROM session_share WHERE session_id = ?`).all(sessionId) as Record[]; + const rows = db + .prepare(`SELECT ${SHARE_COLUMNS.join(', ')} FROM session_share WHERE session_id = ?`) + .all(sessionId) as Record[]; return rows.map((r) => r as unknown as SessionShare); } +// EN: List session IDs using an already-open DB handle (avoids open/close overhead) +// RU: Список ID сессий через уже открытый DB handle (без лишних open/close) +export function listSessionIdsFromHandle(db: DatabaseSync): string[] { + const rows = db.prepare('SELECT id FROM session').all() as { id: string }[]; + return rows.map((r) => r.id); +} + +export function listSessionIdsFromDB(dbPath: string): string[] { + if (!fs.existsSync(dbPath)) return []; + const db = openDB(dbPath); + try { + return listSessionIdsFromHandle(db); + } finally { + db.close(); + } +} + +export function listSessionIdsFromDir(dir: string): string[] { + if (!fs.existsSync(dir)) return []; + return fs + .readdirSync(dir) + .filter((f) => f.endsWith('.json')) + .map((f) => f.replace(/\.json$/, '')); +} + +// EN: Read session + all relations (messages, parts, todos, shares) via open handle +// RU: Чтение сессии + всех связей (сообщения, части, todo, шары) через открытый handle +export function readSessionFromHandle(db: DatabaseSync, id: string): Session | null { + const meta = readSessionMeta(db, id); + if (!meta) return null; + return { + session: meta, + messages: readMessages(db, id), + session_messages: readSessionMessages(db, id), + todos: readTodos(db, id), + session_shares: readShares(db, id), + }; +} + +export function readSessionFromDB(dbPath: string, id: string): Session | null { + if (!fs.existsSync(dbPath)) return null; + const db = openDB(dbPath); + try { + return readSessionFromHandle(db, id); + } finally { + db.close(); + } +} + +export function readSessionFromFile(dir: string, id: string): Session | null { + const filePath = path.join(dir, `${id}.json`); + if (!fs.existsSync(filePath)) return null; + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const parsed = JSON.parse(content); + + if (Array.isArray(parsed.session) && parsed.columns?.session) { + const session = arrayToObject(parsed.session, parsed.columns.session); + const messages = (parsed.message ?? []).map((row: unknown[]) => { + const msg = arrayToObject(row, parsed.columns.message); + (msg as Record).parts = (parsed.parts ?? []) + .filter((p: unknown[]) => p[1] === msg.id) + .map((p: unknown[]) => arrayToObject(p, parsed.columns.parts)); + return msg; + }); + const session_messages = (parsed.session_messages ?? []).map((row: unknown[]) => + arrayToObject(row, parsed.columns.session_message) + ); + return { + session: session as unknown as SessionMeta, + messages, + session_messages, + todos: [], + session_shares: [], + } as Session; + } + + return { + session: parsed.session ?? parsed, + messages: parsed.messages ?? [], + session_messages: parsed.session_messages ?? [], + todos: parsed.todos ?? [], + session_shares: parsed.session_shares ?? [], + } as Session; + } catch { + // EN: Backup corrupted file before returning null (data recovery safety net) + // RU: Бэкап повреждённого файла перед возвратом null (страховка от потери данных) + try { + const brokenPath = `${filePath}.broken`; + if (!fs.existsSync(brokenPath)) { + fs.copyFileSync(filePath, brokenPath); + } + } catch { + // can't backup either — ignore + } + return null; + } +} + export function readSessionsFromDB(dbPath: string): Session[] { if (!fs.existsSync(dbPath)) return []; const db = openDB(dbPath); @@ -202,22 +362,6 @@ export function readSessionsFromDir(dir: string): Session[] { }); } -function deleteSession(db: DatabaseSync, id: string): void { - try { - const msgIds = db.prepare('SELECT id FROM message WHERE session_id = ?').all(id) as { id: string }[]; - for (const { id: mid } of msgIds) { - db.prepare('DELETE FROM part WHERE message_id = ?').run(mid); - } - db.prepare('DELETE FROM message WHERE session_id = ?').run(id); - db.prepare('DELETE FROM session_message WHERE session_id = ?').run(id); - db.prepare('DELETE FROM todo WHERE session_id = ?').run(id); - db.prepare('DELETE FROM session_share WHERE session_id = ?').run(id); - db.prepare('DELETE FROM session WHERE id = ?').run(id); - } catch (e) { - throw new Error(`deleteSession failed for session ${id}: ${e}`); - } -} - function asSQLValue(val: unknown): string | number | null { if (val === null || val === undefined) return null; if (typeof val === 'string') return val; @@ -227,9 +371,13 @@ function asSQLValue(val: unknown): string | number | null { } function upsertSession(db: DatabaseSync, s: SessionMeta): void { - const values = SESSION_COLUMNS.map((col) => asSQLValue((s as unknown as Record)[col])); + const values = SESSION_COLUMNS.map((col) => + asSQLValue((s as unknown as Record)[col]) + ); try { - db.prepare(`INSERT OR REPLACE INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${SESSION_COLUMNS.map(() => '?').join(',')})`).run(...values); + db.prepare( + `INSERT OR REPLACE INTO session (${SESSION_COLUMNS.join(', ')}) VALUES (${SESSION_COLUMNS.map(() => '?').join(',')})` + ).run(...values); } catch (e) { throw new Error(`upsertSession failed for session ${s.id}: ${e}`); } @@ -237,7 +385,9 @@ function upsertSession(db: DatabaseSync, s: SessionMeta): void { function upsertMessages(db: DatabaseSync, messages: Message[]): void { const placeholders = MESSAGE_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT OR REPLACE INTO message (${MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare( + `INSERT OR REPLACE INTO message (${MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})` + ); for (const msg of messages) { try { stmt.run(v(msg.id), v(msg.session_id), v(msg.time_created), v(msg.time_updated), v(msg.data)); @@ -249,10 +399,19 @@ function upsertMessages(db: DatabaseSync, messages: Message[]): void { function upsertParts(db: DatabaseSync, parts: Part[]): void { const placeholders = PART_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT OR REPLACE INTO part (${PART_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare( + `INSERT OR REPLACE INTO part (${PART_COLUMNS.join(', ')}) VALUES (${placeholders})` + ); for (const part of parts) { try { - stmt.run(v(part.id), v(part.message_id), v(part.session_id), v(part.time_created), v(part.time_updated), v(part.data)); + stmt.run( + v(part.id), + v(part.message_id), + v(part.session_id), + v(part.time_created), + v(part.time_updated), + v(part.data) + ); } catch (e) { throw new Error(`upsertParts failed for part ${part.id}: ${e}`); } @@ -265,33 +424,62 @@ function v(val: unknown): string | number | null { function upsertSessionMessages(db: DatabaseSync, items: SessionMessage[]): void { const placeholders = SESSION_MESSAGE_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT OR REPLACE INTO session_message (${SESSION_MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare( + `INSERT OR REPLACE INTO session_message (${SESSION_MESSAGE_COLUMNS.join(', ')}) VALUES (${placeholders})` + ); for (const sm of items) { - stmt.run(v(sm.id), v(sm.session_id), v(sm.type), v(sm.time_created), v(sm.time_updated), v(sm.data)); + stmt.run( + v(sm.id), + v(sm.session_id), + v(sm.type), + v(sm.time_created), + v(sm.time_updated), + v(sm.data) + ); } } function upsertTodos(db: DatabaseSync, items: Todo[]): void { const placeholders = TODO_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT OR REPLACE INTO todo (${TODO_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare( + `INSERT OR REPLACE INTO todo (${TODO_COLUMNS.join(', ')}) VALUES (${placeholders})` + ); for (const todo of items) { - stmt.run(v(todo.session_id), v(todo.content), v(todo.status), v(todo.priority), v(todo.position), v(todo.time_created), v(todo.time_updated)); + stmt.run( + v(todo.session_id), + v(todo.content), + v(todo.status), + v(todo.priority), + v(todo.position), + v(todo.time_created), + v(todo.time_updated) + ); } } function upsertShares(db: DatabaseSync, items: SessionShare[]): void { const placeholders = SHARE_COLUMNS.map(() => '?').join(', '); - const stmt = db.prepare(`INSERT OR REPLACE INTO session_share (${SHARE_COLUMNS.join(', ')}) VALUES (${placeholders})`); + const stmt = db.prepare( + `INSERT OR REPLACE INTO session_share (${SHARE_COLUMNS.join(', ')}) VALUES (${placeholders})` + ); for (const share of items) { - stmt.run(v(share.session_id), v(share.id), v(share.secret), v(share.url), v(share.time_created), v(share.time_updated)); + stmt.run( + v(share.session_id), + v(share.id), + v(share.secret), + v(share.url), + v(share.time_created), + v(share.time_updated) + ); } } -export function writeSessionsToDB(dbPath: string, sessions: Session[]): void { +// EN: Batch-write multiple sessions in one transaction (avoids per-session DB open/close) +// RU: Пакетная запись нескольких сессий одной транзакцией (без per-session open/close БД) +export function writeSessionsToHandle(db: DatabaseSync, sessions: Session[]): void { if (sessions.length === 0) return; - const db = openDB(dbPath); + db.exec('BEGIN TRANSACTION'); try { - db.exec('BEGIN TRANSACTION'); for (const s of sessions) { upsertSession(db, s.session); upsertMessages(db, s.messages); @@ -306,15 +494,30 @@ export function writeSessionsToDB(dbPath: string, sessions: Session[]): void { } catch (error) { db.exec('ROLLBACK'); throw error; + } +} + +export function writeSessionsToDB(dbPath: string, sessions: Session[]): void { + if (sessions.length === 0) return; + const db = openDB(dbPath); + try { + writeSessionsToHandle(db, sessions); } finally { db.close(); } } +// EN: Write single session to JSON file (compact format, no pretty-print — saves ~30% disk) +// RU: Запись одной сессии в JSON-файл (compact, без pretty-print — экономия ~30% места) +export function writeSessionToFile(dir: string, session: Session): void { + fs.mkdirSync(dir, { recursive: true }); + const filePath = path.join(dir, `${session.session.id}.json`); + fs.writeFileSync(filePath, JSON.stringify(session), 'utf-8'); +} + export function writeSessionsToDir(dir: string, sessions: Session[]): void { fs.mkdirSync(dir, { recursive: true }); for (const s of sessions) { - const filePath = path.join(dir, `${s.session.id}.json`); - fs.writeFileSync(filePath, JSON.stringify(s, null, 2), 'utf-8'); + writeSessionToFile(dir, s); } } diff --git a/src/sync/session-merge.ts b/src/sync/session-merge.ts index e31cd0a..1f39375 100644 --- a/src/sync/session-merge.ts +++ b/src/sync/session-merge.ts @@ -1,5 +1,15 @@ -import type { Session, Message, Part, SessionMessage, Todo } from './session-db.js'; -import { readSessionsFromDB, readSessionsFromDir, writeSessionsToDB, writeSessionsToDir } from './session-db.js'; +import fs from 'node:fs'; +import { DatabaseSync } from 'node:sqlite'; +import type { Message, Part, Session, Todo } from './session-db.js'; +import { + checkpointDB, + listSessionIdsFromDir, + listSessionIdsFromHandle, + readSessionFromFile, + readSessionFromHandle, + writeSessionsToHandle, + writeSessionToFile, +} from './session-db.js'; export interface SyncSessionResult { total: number; @@ -7,6 +17,8 @@ export interface SyncSessionResult { conflicts: number; } +// EN: Pick the newer record by time_updated (null treated as oldest) +// RU: Выбор более новой записи по time_updated (null считается старым) function pickNewer(a: T, b: T): T { if (!a.time_updated) return b; if (!b.time_updated) return a; @@ -39,7 +51,10 @@ function unionParts(local: Part[], remote: Part[]): Part[] { return [...map.values()]; } -function unionById(local: T[], remote: T[]): T[] { +function unionById( + local: T[], + remote: T[] +): T[] { const map = new Map(); for (const item of local) map.set(item.id, item); for (const item of remote) { @@ -63,6 +78,8 @@ function unionTodos(local: Todo[], remote: Todo[]): Todo[] { return [...map.values()]; } +// EN: Union-merge two session versions — picks newer session metadata + unions all relations by id +// RU: Union-merge двух версий сессии — берёт новую мету, объединяет все связи по id function merge(local: Session, remote: Session): Session { const localT = local.session.time_updated ?? ''; const remoteT = remote.session.time_updated ?? ''; @@ -77,51 +94,60 @@ function merge(local: Session, remote: Session): Session { }; } +// EN: Stream-based session sync — opens DB once, processes sessions one-by-one, batch-writes at end +// EN: Single DB connection eliminates per-session open/close overhead (was N+2 open/close per cycle) +// RU: Потоковая синхронизация — одно открытие БД, обработка сессий по одной, batch-запись в конце +// RU: Одно соединение с БД устраняет per-session накладные расходы (было N+2 open/close за цикл) export async function syncSessions( dbPath: string, sessionsDir: string ): Promise { - const localSessions = readSessionsFromDB(dbPath); - const remoteSessions = readSessionsFromDir(sessionsDir); + const remoteIds = listSessionIdsFromDir(sessionsDir); - const localMap = new Map(); - for (const s of localSessions) localMap.set(s.session.id, s); - - const remoteMap = new Map(); - for (const s of remoteSessions) remoteMap.set(s.session.id, s); - - const allIds = new Set([...localMap.keys(), ...remoteMap.keys()]); - const merged: Session[] = []; - let mergeCount = 0; - let conflictCount = 0; - - for (const id of allIds) { - const local = localMap.get(id); - const remote = remoteMap.get(id); + const dbExists = fs.existsSync(dbPath); + const db = dbExists ? new DatabaseSync(dbPath) : null; + if (db) { + checkpointDB(db); + } - if (!local) { - merged.push(structuredClone(remote!)); - continue; - } - if (!remote) { - merged.push(structuredClone(local)); - continue; + try { + const localIds = db ? listSessionIdsFromHandle(db) : []; + const allIds = new Set([...localIds, ...remoteIds]); + let totalMerged = 0; + const hasRemote = remoteIds.length > 0; + const toWrite: Session[] = []; + + for (const id of allIds) { + const local = db ? readSessionFromHandle(db, id) : null; + const remote = readSessionFromFile(sessionsDir, id); + + let merged: Session; + + if (!local) { + merged = structuredClone(remote as Session); + } else if (!remote) { + merged = structuredClone(local); + } else { + totalMerged++; + merged = merge(local, remote); + } + + if (hasRemote && local) { + toWrite.push(merged); + } + writeSessionToFile(sessionsDir, merged); } - mergeCount++; - merged.push(merge(local, remote)); - } + if (db && toWrite.length > 0) { + writeSessionsToHandle(db, toWrite); + } - if (remoteSessions.length === 0) { - writeSessionsToDir(sessionsDir, merged); - } else { - writeSessionsToDB(dbPath, merged); - writeSessionsToDir(sessionsDir, merged); + return { + total: allIds.size, + merged: totalMerged, + conflicts: 0, + }; + } finally { + db?.close(); } - - return { - total: allIds.size, - merged: mergeCount, - conflicts: conflictCount, - }; } diff --git a/src/sync/shell.ts b/src/sync/shell.ts index 8a21cdc..cbfff22 100644 --- a/src/sync/shell.ts +++ b/src/sync/shell.ts @@ -1,19 +1,25 @@ -import { execSync } from 'node:child_process'; +import { exec } from 'node:child_process'; -export interface ShellProcessPromise extends Promise<{ exitCode: number; stdout: string; stderr: string }> { +export interface ShellProcessPromise + extends Promise<{ exitCode: number; stdout: string; stderr: string }> { quiet(): this; text(): Promise; } export type ShellFn = (strings: TemplateStringsArray, ...values: unknown[]) => ShellProcessPromise; +// EN: Build shell command string — wraps all interpolated values in double quotes +// EN: Prevents path/commit-message breakage on Windows (spaces in paths, etc.) +// RU: Сборка команды — все интерполированные значения оборачиваются в двойные кавычки +// RU: Предотвращает поломку путей/commit message на Windows (пробелы в путях, и т.д.) function buildCommand(strings: TemplateStringsArray, ...values: unknown[]): string { let cmd = ''; for (let i = 0; i < strings.length; i++) { cmd += strings[i]; if (i < values.length) { const v = values[i]; - cmd += typeof v === 'string' ? v : String(v); + const str = typeof v === 'string' ? v : String(v); + cmd += `"${str.replace(/"/g, '\\"')}"`; } } return cmd.trim(); @@ -24,36 +30,48 @@ export function createNodeShell(): ShellFn { const command = buildCommand(strings, ...values); let isQuiet = false; - const run = (): { exitCode: number; stdout: string; stderr: string } => { - try { - const stdout = execSync(command, { encoding: 'utf-8', stdio: isQuiet ? 'pipe' : 'inherit' }); - return { exitCode: 0, stdout: stdout ?? '', stderr: '' }; - } catch (error) { - if (error instanceof Error && 'stdout' in error && 'stderr' in error) { - const execError = error as Error & { stdout: string; stderr: string; status?: number }; - return { - exitCode: execError.status ?? 1, - stdout: execError.stdout ?? '', - stderr: execError.stderr ?? '', - }; + const run = (): Promise<{ exitCode: number; stdout: string; stderr: string }> => { + return new Promise((resolve) => { + const child = exec( + command, + { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }, + (error, stdout, stderr) => { + if (error) { + resolve({ + exitCode: error.code ?? 1, + stdout: stdout ?? '', + stderr: stderr ?? '', + }); + } else { + resolve({ exitCode: 0, stdout: stdout ?? '', stderr: '' }); + } + } + ); + if (!isQuiet) { + child.stdout?.pipe(process.stdout); + child.stderr?.pipe(process.stderr); } - throw error; - } + }); }; let promise: Promise<{ exitCode: number; stdout: string; stderr: string }> | null = null; const getPromise = (): Promise<{ exitCode: number; stdout: string; stderr: string }> => { if (!promise) { - promise = new Promise((resolve) => { - resolve(run()); - }); + promise = run(); } return promise; }; const shellPromise = { + // biome-ignore lint/suspicious/noThenProperty: thenable pattern for async shell execution then( - onfulfilled?: ((value: { exitCode: number; stdout: string; stderr: string }) => TResult1 | PromiseLike) | null, + onfulfilled?: + | ((value: { + exitCode: number; + stdout: string; + stderr: string; + }) => TResult1 | PromiseLike) + | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null ): Promise { return getPromise().then(onfulfilled, onrejected); @@ -63,7 +81,9 @@ export function createNodeShell(): ShellFn { ): Promise<{ exitCode: number; stdout: string; stderr: string } | TResult> { return getPromise().catch(onrejected); }, - finally(onfinally?: (() => void) | null): Promise<{ exitCode: number; stdout: string; stderr: string }> { + finally( + onfinally?: (() => void) | null + ): Promise<{ exitCode: number; stdout: string; stderr: string }> { return getPromise().finally(onfinally); }, get [Symbol.toStringTag]() { @@ -71,7 +91,7 @@ export function createNodeShell(): ShellFn { }, quiet(): ShellProcessPromise { isQuiet = true; - return shellPromise as unknown as ShellProcessPromise; + return this as unknown as ShellProcessPromise; }, text(): Promise { return getPromise().then((r) => r.stdout); diff --git a/src/sync/turso.test.ts b/src/sync/turso.test.ts index cffbee9..9d4cec4 100644 --- a/src/sync/turso.test.ts +++ b/src/sync/turso.test.ts @@ -87,14 +87,14 @@ describe('isRetryableTursoError', () => { describe('path helpers', () => { it('resolves credential path and session db paths', () => { const locations = createLocations(); - expect(resolveTursoCredentialPath(locations)).toBe( + const p = (s: string) => s.replace(/\\/g, '/'); + expect(p(resolveTursoCredentialPath(locations))).toBe( '/home/test/.local/share/opencode/opencode-synced/turso-session.json' ); - expect(resolveSessionDbPaths(locations)).toEqual({ - dbPath: '/home/test/.local/share/opencode/opencode.db', - walPath: '/home/test/.local/share/opencode/opencode.db-wal', - shmPath: '/home/test/.local/share/opencode/opencode.db-shm', - }); + const result = resolveSessionDbPaths(locations); + expect(p(result.dbPath)).toBe('/home/test/.local/share/opencode/opencode.db'); + expect(p(result.walPath)).toBe('/home/test/.local/share/opencode/opencode.db-wal'); + expect(p(result.shmPath)).toBe('/home/test/.local/share/opencode/opencode.db-shm'); }); }); From 111e219564acb8eeabb7e407121a61ab3f130da1 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Sat, 23 May 2026 10:18:09 +0300 Subject: [PATCH 15/17] fix: cross-platform Windows compatibility and database fixes --- AGENTS.md | 87 +++------------------- package.json | 6 +- src/command/sync-enable-secrets.md | 15 +--- src/command/sync-init.md | 2 +- src/command/sync-link.md | 2 +- src/command/sync-pull.md | 10 +-- src/command/sync-push.md | 11 +-- src/command/sync-resolve.md | 10 +-- src/command/sync-secrets-pull.md | 9 +-- src/command/sync-secrets-push.md | 9 +-- src/command/sync-secrets-status.md | 9 +-- src/command/sync-sessions-backend.md | 3 +- src/command/sync-sessions-cleanup-git.md | 7 +- src/command/sync-sessions-migrate-turso.md | 3 +- src/command/sync-sessions-setup-turso.md | 3 +- src/command/sync-status.md | 9 +-- 16 files changed, 40 insertions(+), 155 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d458913..b4dfe32 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,16 +1,15 @@ # AGENTS.md -## Language -- Все ответы пользователю — ТОЛЬКО на русском языке. Никогда не использовать английский. +## Build & Test Commands -## Build & Test Commands (Windows / Node.js) - -- **Build**: `npx tsc -p tsconfig.build.json && copyfiles -u 1 "src/command/**/*" dist/` -- **Test**: `npx vitest run` -- **Single Test**: `npx vitest run --reporter=verbose src/sync/paths.test.ts` -- **Lint**: `npx biome lint .` -- **Check**: `npx biome check --write .` -- **Deploy**: copy `dist/` to `C:\Users\Aleks\.config\opencode\plugins\opencode-synced\` +- **Build**: `mise run build` or `bun build ./src/index.ts --outdir dist --target bun` +- **Test**: `mise run test` or `bun test` +- **Single Test**: `bun test BackgroundTask.test.ts` (use file glob pattern) +- **Watch Mode**: `bun test --watch` +- **Lint**: `mise run lint` or `bun run lint` (biome) +- **Fix Lint**: `mise run lint:fix` or `bun run lint:fix` (biome --write) +- **Format**: `mise run format` or `bun run format` (biome format --write) +- **Check (Lint + Format)**: `bun run check` (biome check --write) ## PR & Commit Guidelines @@ -71,71 +70,3 @@ - **Type**: ES Module package for opencode plugin system - **Target**: Bun runtime, ES2021+ - **Purpose**: Sync global opencode config across machines via GitHub, with optional secrets support (e.g., 1Password backend) - -## OpenCode Desktop Context (Windows) - -- OpenCode v1.15.7 from anomalyco/opencode (Electron/Node.js, not Bun Terminal) -- Desktop config: `C:\Users\Aleks\.config\opencode\` (XDG-style, NOT `%APPDATA%`) -- Plugin API: local at `C:\Users\Aleks\.config\opencode\node_modules\@opencode-ai\plugin\` v1.4.7 -- `ctx.$` is `undefined` on Desktop (Bun shell not available), but `createNodeShell()` fallback exists -- Plugin config: `C:\Users\Aleks\.config\opencode\opencode-synced.jsonc` tells which GitHub repo to sync -- Session DB: `C:\Users\Aleks\.local\share\opencode\opencode.db` (SQLite) -- Desktop projects store: `%APPDATA%\ai.opencode.desktop\opencode.global.dat` - -## Build Output Notes - -- **compact JSON**: All session files use `JSON.stringify(session)` without `null, 2` (pretty-print). This saves ~30% disk space and reduces git diff noise. Reading both formats is supported (single-session and old columnar). -- **Broken JSON recovery**: If a session JSON file can't be parsed, a `.broken` backup is created before returning null (prevents total data loss). - -## Cross-Platform (Windows / Linux / macOS) - -- `platformJoin(platform, ...parts)` in `paths.ts` — uses `path.posix.join` or `path.win32.join` based on `platform` parameter -- `expandHome()` and `normalizePath()` — always produce forward slashes, backslashes normalized before comparison -- All `buildSyncPlan`/`resolveXdgPaths`/`resolveSyncLocations` tests pass on Windows (79/79) - -## Project State (23.05.2026) - -### Done -- XDG paths on all platforms (no %APPDATA%) -- Notifications (toasts + logs) on key lifecycle points -- Session sync: union-merge per record (session-db.ts, session-merge.ts) -- Projects sync: opencode.global.dat merge (projects-merge.ts) -- Auto-commit pending changes on startup instead of bailing out -- Removed AI commit message generation → date-based messages -- `createNodeShell()`: async `exec` instead of `execSync`, proper arg quoting -- CONFIG_DIRS includes `'plugins'` and `'commands'` for Desktop compatibility -- HEAD cache skip: если HEAD не изменился и нет локальных изменений — пропускаем git fetch (экономия сети) -- skipIfBusy: file-based exclusive lock предотвращает конкурентные sync -- Дебаунс: startup sync обёрнут в skipIfBusy -- Двуязычные комментарии EN/RU в ключевых модулях -- Двуязычные `description` в 14 `.md` командах -- `parseFrontmatter()` — BOM + CRLF + fallback first-line fix -- `platformJoin()` helper для кроссплатформенных тестов -- `expandHome()` — всегда forward slashes (не `path.join`) -- Все 79 тестов проходят на Windows (0 known failures) -- `asSQLValue()` — обёртка всех SQLite-параметров -- `INSERT OR REPLACE` вместо DELETE+INSERT -- Пропуск `writeSessionsToDB` при пустой remote-директории -- `readSessionsFromDir` — поддержка старого колоночного формата JSON -- Убрано копирование `opencode.db` и `storage/` из sync plan (сессии → syncSessions) - -### Session Sync Optimizations -1. **Compact JSON** — `JSON.stringify(session)` без `null, 2` (экономия ~30%) -2. **WAL checkpoint один раз** — вынесен из `openDB()` в `syncSessions()`, однакратно за цикл -3. **Background git push** — `pushBranch(...).catch(...)`, не блокирует стартап -4. **Единый DB handle** — `syncSessions()` открывает SQLite один раз, читает/пишет через handle-варианты функций -5. **Batch DB writes** — все мержнутые сессии пишутся одним `BEGIN…COMMIT`, а не по одной -6. **Recovery битых JSON** — перед возвратом `null` создаётся `.broken`-копия файла - -### Known Issues -- **Лог Desktop** — кольцевой буфер ~12 строк. MCP-ошибки agentmemory/TestSprite (`prompts/list`) забивают его за ~39 секунд, вытесняя логи плагина. Не наша проблема. -- **Worker threads** — SQLite синхронный (`node:sqlite` DatabaseSync), но с единым handle + batch уже быстро. Если понадобится — вынести в `worker_threads`. - -### Removed Upstream Features -- `generateCommitMessage` (commit.ts) — LLM для commit message не используется. Сообщение = `Sync opencode config (YYYY-MM-DD)` -- `opencode.db` исключён из sync plan (предотвращает затирание сессий при pull с другой машины). Сессии → per-session JSON merge (`syncSessions`) - -### Deploy -1. `npx tsc -p tsconfig.build.json && npx copyfiles -u 1 "src/command/**/*" dist/` -2. `robocopy dist "C:\Users\Aleks\.config\opencode\plugins\opencode-synced\dist" /MIR /NFL /NDL /NJH /NJS /NC /NS /NP` -3. Restart OpenCode Desktop diff --git a/package.json b/package.json index f17fd43..6b5f16c 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,14 @@ "access": "public" }, "files": ["dist"], + "dependencies": { + "@opencode-ai/plugin": "1.0.85" + }, "devDependencies": { "@biomejs/biome": "2.3.10", "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", - "@opencode-ai/plugin": "file:C:\\Users\\Aleks\\.config\\opencode\\node_modules\\@opencode-ai\\plugin", - "@types/node": "^22.19.19", + "@types/node": "^20.11.5", "bun-types": "latest", "husky": "^9.1.7", "lint-staged": "^16.2.7", diff --git a/src/command/sync-enable-secrets.md b/src/command/sync-enable-secrets.md index fb7225c..2a8b228 100644 --- a/src/command/sync-enable-secrets.md +++ b/src/command/sync-enable-secrets.md @@ -1,14 +1,7 @@ --- -description: Enable secrets sync / Включить синхронизацию секретов (требуется приватный репозиторий) +description: Enable secrets sync (private repo required) --- -You MUST call the `opencode_sync` tool with `command="enable-secrets"`. -Do not answer with plain text only. - -Argument handling: -- If the user supplies extra secret paths, pass them via `extraSecretPaths`. -- If they want MCP secrets committed in a private repo, pass `includeMcpSecrets: true`. - -Reminder: -- Enabling secrets requires the sync repo to be private — the tool will enforce this. -- Secrets are synced via a configured backend (e.g., 1Password), not stored in git directly. +Use the opencode_sync tool with command "enable-secrets". +If the user supplies extra secret paths, pass them via extraSecretPaths. +If they want MCP secrets committed in a private repo, pass includeMcpSecrets: true. diff --git a/src/command/sync-init.md b/src/command/sync-init.md index 3fb46d2..50ebe35 100644 --- a/src/command/sync-init.md +++ b/src/command/sync-init.md @@ -1,5 +1,5 @@ --- -description: Initialize opencode-synced configuration / Инициализировать конфигурацию синхронизации +description: Initialize opencode-synced configuration --- You MUST call the `opencode_sync` tool with `command="init"`. diff --git a/src/command/sync-link.md b/src/command/sync-link.md index b66ea2b..9c64e64 100644 --- a/src/command/sync-link.md +++ b/src/command/sync-link.md @@ -1,5 +1,5 @@ --- -description: Link to an existing sync repo / Привязать компьютер к существующему репозиторию синхронизации +description: Link this computer to an existing sync repo --- You MUST call the `opencode_sync` tool with `command="link"`. diff --git a/src/command/sync-pull.md b/src/command/sync-pull.md index 6491980..66a6a5f 100644 --- a/src/command/sync-pull.md +++ b/src/command/sync-pull.md @@ -1,10 +1,6 @@ --- -description: Pull and apply synced config / Загрузить и применить синхронизированную конфигурацию +description: Pull and apply synced opencode config --- -You MUST call the `opencode_sync` tool with `command="pull"`. -Do not answer with plain text only. - -Reminder: -- Pull applies remote config to local — after a successful pull, tell the user to restart opencode. -- If the local repo has uncommitted changes, pull will fail — suggest /sync-resolve first. +Use the opencode_sync tool with command "pull". +If updates are applied, remind the user to restart opencode. diff --git a/src/command/sync-push.md b/src/command/sync-push.md index 89f9172..cdc5a12 100644 --- a/src/command/sync-push.md +++ b/src/command/sync-push.md @@ -1,10 +1,5 @@ ---- -description: Push local config / Отправить локальную конфигурацию +--- +description: Push local opencode config to the sync repo --- -You MUST call the `opencode_sync` tool with `command="push"`. -Do not answer with plain text only. - -Reminder: -- The tool will auto-commit and push local changes to the sync repo. -- If there are uncommitted changes in the local repo, the push may fail — suggest /sync-resolve. +Use the opencode_sync tool with command "push". diff --git a/src/command/sync-resolve.md b/src/command/sync-resolve.md index 6ead2a0..0f8470a 100644 --- a/src/command/sync-resolve.md +++ b/src/command/sync-resolve.md @@ -1,11 +1,5 @@ --- -description: Resolve uncommitted changes / Разрешить незакоммиченные изменения в репозитории +description: Resolve uncommitted changes in sync repo --- -You MUST call the `opencode_sync` tool with `command="resolve"`. -Do not answer with plain text only. - -Behavior: -- The tool analyzes uncommitted changes using AI and decides whether to commit or discard them. -- After resolution, the user can retry the failed command. -- If AI analysis is not available, the tool falls back to manual resolution instructions. +Use the opencode_sync tool with command "resolve" to automatically resolve uncommitted changes in the local sync repository using AI. diff --git a/src/command/sync-secrets-pull.md b/src/command/sync-secrets-pull.md index fa927e1..a3bcb1b 100644 --- a/src/command/sync-secrets-pull.md +++ b/src/command/sync-secrets-pull.md @@ -1,10 +1,5 @@ --- -description: Pull secrets from backend / Загрузить секреты из настроенного бэкенда +description: Pull secrets from the configured backend --- -You MUST call the `opencode_sync` tool with `command="secrets-pull"`. -Do not answer with plain text only. - -Behavior: -- Pulls secrets from the configured backend (e.g., 1Password) and applies them locally. -- Requires a secrets backend to be configured. +Use the opencode_sync tool with command "secrets-pull". diff --git a/src/command/sync-secrets-push.md b/src/command/sync-secrets-push.md index 055d85a..4b4e834 100644 --- a/src/command/sync-secrets-push.md +++ b/src/command/sync-secrets-push.md @@ -1,10 +1,5 @@ --- -description: Push secrets to backend / Отправить секреты в настроенный бэкенд +description: Push secrets to the configured backend --- -You MUST call the `opencode_sync` tool with `command="secrets-push"`. -Do not answer with plain text only. - -Behavior: -- Pushes secrets to the configured backend (e.g., 1Password). -- Requires a secrets backend to be configured. +Use the opencode_sync tool with command "secrets-push". diff --git a/src/command/sync-secrets-status.md b/src/command/sync-secrets-status.md index 59b8c77..67c15ad 100644 --- a/src/command/sync-secrets-status.md +++ b/src/command/sync-secrets-status.md @@ -1,10 +1,5 @@ --- -description: Show secrets backend status / Показать статус бэкенда секретов +description: Show secrets backend status --- -You MUST call the `opencode_sync` tool with `command="secrets-status"`. -Do not answer with plain text only. - -Behavior: -- Shows the current status of the secrets backend (e.g., last sync, backend type). -- Requires a secrets backend to be configured. +Use the opencode_sync tool with command "secrets-status". diff --git a/src/command/sync-sessions-backend.md b/src/command/sync-sessions-backend.md index 9307012..0f2ab6f 100644 --- a/src/command/sync-sessions-backend.md +++ b/src/command/sync-sessions-backend.md @@ -1,9 +1,8 @@ --- -description: Switch session sync backend (git/turso) / Переключить бэкенд синхронизации сессий +description: Switch session sync backend between git and turso --- You MUST call the `opencode_sync` tool with `command="sessions-backend"`. -Do not answer with plain text only. Argument handling: - `$ARGUMENTS` must be either `git` or `turso`. diff --git a/src/command/sync-sessions-cleanup-git.md b/src/command/sync-sessions-cleanup-git.md index 4d93b98..f051872 100644 --- a/src/command/sync-sessions-cleanup-git.md +++ b/src/command/sync-sessions-cleanup-git.md @@ -1,10 +1,7 @@ --- -description: Remove deprecated git session artifacts / Удалить устаревшие git-артефакты сессий +description: Remove deprecated git session artifacts after Turso migration --- You MUST call the `opencode_sync` tool with `command="sessions-cleanup-git"`. -Do not answer with plain text only. -Reminder: -- Use this only after sessions are running on Turso and the user confirms fallback artifacts are no longer needed. -- This removes data/sessions/*.json and data/opencode.db from the sync repo. +Use this only after sessions are running on Turso and the user confirms fallback artifacts are no longer needed. diff --git a/src/command/sync-sessions-migrate-turso.md b/src/command/sync-sessions-migrate-turso.md index f756d2c..6867060 100644 --- a/src/command/sync-sessions-migrate-turso.md +++ b/src/command/sync-sessions-migrate-turso.md @@ -1,9 +1,8 @@ --- -description: Migrate session sync to Turso / Перенести синхронизацию сессий из git в Turso +description: Migrate session sync from git artifacts to Turso backend --- You MUST call the `opencode_sync` tool with `command="sessions-migrate-turso"`. -Do not answer with plain text only. Behavior: - Ensure Turso setup is complete. diff --git a/src/command/sync-sessions-setup-turso.md b/src/command/sync-sessions-setup-turso.md index 0bd0602..fd43664 100644 --- a/src/command/sync-sessions-setup-turso.md +++ b/src/command/sync-sessions-setup-turso.md @@ -1,9 +1,8 @@ --- -description: Setup Turso for session sync / Настроить Turso для синхронизации сессий +description: Install/auth/provision Turso for session sync on this machine --- You MUST call the `opencode_sync` tool with `command="sessions-setup-turso"`. -Do not answer with plain text only. Behavior: - Run Turso CLI install if missing. diff --git a/src/command/sync-status.md b/src/command/sync-status.md index 048bdd8..3a53e12 100644 --- a/src/command/sync-status.md +++ b/src/command/sync-status.md @@ -1,10 +1,5 @@ --- -description: Show sync status / Показать статус синхронизации +description: Show opencode-synced status --- -You MUST call the `opencode_sync` tool with `command="status"`. -Do not answer with plain text only. - -Reminder: -- Report the results to the user in a readable format (repo, branch, last push/pull, working tree state). -- If sessions sync is enabled, mention the session backend (git or turso) and last sync times. +Use the opencode_sync tool with command "status" and report the results. From d1f4ef58bc7c66ab98b6de9754efbe459cab0a0b Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Sat, 23 May 2026 10:18:24 +0300 Subject: [PATCH 16/17] fix: remove package-lock.json from tracked files --- .gitignore | 1 + package-lock.json | 3353 --------------------------------------------- 2 files changed, 1 insertion(+), 3353 deletions(-) delete mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index 2e993bb..1a9dbfd 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ coverage/ __pycache__/ opencode-plugin-template/ opencode-docs-*/ +package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 5673eb7..0000000 --- a/package-lock.json +++ /dev/null @@ -1,3353 +0,0 @@ -{ - "name": "opencode-synced", - "version": "0.9.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "opencode-synced", - "version": "0.9.0", - "devDependencies": { - "@biomejs/biome": "2.3.10", - "@commitlint/cli": "^20.2.0", - "@commitlint/config-conventional": "^20.2.0", - "@opencode-ai/plugin": "file:C:\\Users\\Aleks\\.config\\opencode\\node_modules\\@opencode-ai\\plugin", - "@types/node": "^22.19.19", - "bun-types": "latest", - "husky": "^9.1.7", - "lint-staged": "^16.2.7", - "typescript": "^5.9.2", - "vitest": "^3.2.4" - } - }, - "C:/Users/Aleks/.config/opencode/node_modules/@opencode-ai/plugin": { - "version": "1.4.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@opencode-ai/sdk": "1.4.7", - "effect": "4.0.0-beta.48", - "zod": "4.1.8" - }, - "devDependencies": { - "@opentui/core": "0.1.99", - "@opentui/solid": "0.1.99", - "@tsconfig/node22": "22.0.2", - "@types/node": "22.13.9", - "@typescript/native-preview": "7.0.0-dev.20251207.1", - "typescript": "5.8.2" - }, - "peerDependencies": { - "@opentui/core": ">=0.1.99", - "@opentui/solid": ">=0.1.99" - }, - "peerDependenciesMeta": { - "@opentui/core": { - "optional": true - }, - "@opentui/solid": { - "optional": true - } - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@biomejs/biome": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.10.tgz", - "integrity": "sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.3.10", - "@biomejs/cli-darwin-x64": "2.3.10", - "@biomejs/cli-linux-arm64": "2.3.10", - "@biomejs/cli-linux-arm64-musl": "2.3.10", - "@biomejs/cli-linux-x64": "2.3.10", - "@biomejs/cli-linux-x64-musl": "2.3.10", - "@biomejs/cli-win32-arm64": "2.3.10", - "@biomejs/cli-win32-x64": "2.3.10" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.10.tgz", - "integrity": "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.10.tgz", - "integrity": "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.10.tgz", - "integrity": "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.10.tgz", - "integrity": "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.10.tgz", - "integrity": "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.10.tgz", - "integrity": "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.10.tgz", - "integrity": "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.10.tgz", - "integrity": "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@commitlint/cli": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.3.tgz", - "integrity": "sha512-OJdL0EXWD5y9LPa0nr/geOwzaS8BsdaybKkcloB0JgsguGxNv2R+hC2FTPqrAcprg35zF33KOQerY0x8W1aesA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/format": "^20.5.0", - "@commitlint/lint": "^20.5.3", - "@commitlint/load": "^20.5.3", - "@commitlint/read": "^20.5.0", - "@commitlint/types": "^20.5.0", - "tinyexec": "^1.0.0", - "yargs": "^17.0.0" - }, - "bin": { - "commitlint": "cli.js" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/config-conventional": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.3.tgz", - "integrity": "sha512-j34Qqeaa152chJgz2ysyk0BCpHenJn1lV0Rx0VXf8k3ccQcED+48EZrzMvo9jLmJUyBrrBwvu89I+2er4gW7QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.5.0", - "conventional-changelog-conventionalcommits": "^9.2.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/config-validator": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.5.0.tgz", - "integrity": "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.5.0", - "ajv": "^8.11.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/ensure": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.3.tgz", - "integrity": "sha512-4i4AgNvH62owG9MwSiWKrle7HGNpBHHdLnWFIp5fTsHUYe5kRuh15t08L/0pdbbrRk8JKXQxxN4hZQcn+szkrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.5.0", - "es-toolkit": "^1.46.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/execute-rule": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", - "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/format": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.5.0.tgz", - "integrity": "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.5.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/is-ignored": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.5.0.tgz", - "integrity": "sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.5.0", - "semver": "^7.6.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/lint": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.3.tgz", - "integrity": "sha512-M7JbWBNr2gXKaPc4i/KipsuW1gkDHpj35KPjWtKy3Z+2AQw5wu1gBi1LIO0uoaij67CqY4K8PxPZSGens4evCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/is-ignored": "^20.5.0", - "@commitlint/parse": "^20.5.0", - "@commitlint/rules": "^20.5.3", - "@commitlint/types": "^20.5.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/load": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.3.tgz", - "integrity": "sha512-1FDZWuKyu98Myb8i7Tp31jPU2rZpOwAdYRyJcy2KoGg7Xk2A+bgHN8smhMaaNSNkmE8fwt53BokywZq8Gv/5XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/config-validator": "^20.5.0", - "@commitlint/execute-rule": "^20.0.0", - "@commitlint/resolve-extends": "^20.5.3", - "@commitlint/types": "^20.5.0", - "cosmiconfig": "^9.0.1", - "cosmiconfig-typescript-loader": "^6.1.0", - "es-toolkit": "^1.46.0", - "is-plain-obj": "^4.1.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/message": { - "version": "20.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz", - "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/parse": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.5.0.tgz", - "integrity": "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^20.5.0", - "conventional-changelog-angular": "^8.2.0", - "conventional-commits-parser": "^6.3.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/read": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.5.0.tgz", - "integrity": "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/top-level": "^20.4.3", - "@commitlint/types": "^20.5.0", - "git-raw-commits": "^5.0.0", - "minimist": "^1.2.8", - "tinyexec": "^1.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/resolve-extends": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.3.tgz", - "integrity": "sha512-+ogW9v/u9JqpvAgTrLra/YTFo0KkjU6iNblF89pPsj4NebNc+DAWctsludwezI8YnsjBmfHpApSwcXprN/f/ew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/config-validator": "^20.5.0", - "@commitlint/types": "^20.5.0", - "es-toolkit": "^1.46.0", - "global-directory": "^5.0.0", - "import-meta-resolve": "^4.0.0", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/rules": { - "version": "20.5.3", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.3.tgz", - "integrity": "sha512-MPlMnb9D3wbszYMp+1hPtuhtPJndRo6I6yfkZVA4+jR8w7Kqp0u2u/Y+gzbaItx5Lltq5rw7FSZQWJMoXUC4NQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/ensure": "^20.5.3", - "@commitlint/message": "^20.4.3", - "@commitlint/to-lines": "^20.0.0", - "@commitlint/types": "^20.5.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/to-lines": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", - "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/top-level": { - "version": "20.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz", - "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@commitlint/types": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.5.0.tgz", - "integrity": "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-commits-parser": "^6.3.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=v18" - } - }, - "node_modules/@conventional-changelog/git-client": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.7.0.tgz", - "integrity": "sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@simple-libs/child-process-utils": "^1.0.0", - "@simple-libs/stream-utils": "^1.2.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "conventional-commits-filter": "^5.0.0", - "conventional-commits-parser": "^6.4.0" - }, - "peerDependenciesMeta": { - "conventional-commits-filter": { - "optional": true - }, - "conventional-commits-parser": { - "optional": true - } - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@opencode-ai/plugin": { - "resolved": "C:/Users/Aleks/.config/opencode/node_modules/@opencode-ai/plugin", - "link": true - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", - "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", - "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", - "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", - "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", - "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", - "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", - "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", - "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", - "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", - "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", - "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", - "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", - "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", - "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", - "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", - "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", - "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", - "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", - "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", - "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", - "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", - "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", - "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", - "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", - "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@simple-libs/child-process-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", - "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@simple-libs/stream-utils": "^1.2.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" - } - }, - "node_modules/@simple-libs/stream-utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", - "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://ko-fi.com/dangreen" - } - }, - "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": "22.19.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", - "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", - "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true, - "license": "MIT" - }, - "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/bun-types": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.14.tgz", - "integrity": "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", - "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^8.0.0", - "string-width": "^8.2.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/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/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/conventional-changelog-angular": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.1.tgz", - "integrity": "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.1.tgz", - "integrity": "sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/conventional-commits-parser": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", - "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@simple-libs/stream-utils": "^1.2.0", - "meow": "^13.0.0" - }, - "bin": { - "conventional-commits-parser": "dist/cli/index.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cosmiconfig": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", - "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", - "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jiti": "2.6.1" - }, - "engines": { - "node": ">=v18" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=9", - "typescript": ">=5" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-toolkit": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", - "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", - "dev": true, - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks" - ] - }, - "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "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/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/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-raw-commits": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz", - "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@conventional-changelog/git-client": "^2.6.0", - "meow": "^13.0.0" - }, - "bin": { - "git-raw-commits": "src/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/global-directory": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", - "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "6.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/husky": { - "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", - "dev": true, - "license": "MIT", - "bin": { - "husky": "bin.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "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/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lint-staged": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", - "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^14.0.3", - "listr2": "^9.0.5", - "picomatch": "^4.0.3", - "string-argv": "^0.3.2", - "tinyexec": "^1.0.4", - "yaml": "^2.8.2" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": ">=20.17" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/listr2": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", - "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^5.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "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/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "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.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "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.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rollup": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", - "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.4", - "@rollup/rollup-android-arm64": "4.60.4", - "@rollup/rollup-darwin-arm64": "4.60.4", - "@rollup/rollup-darwin-x64": "4.60.4", - "@rollup/rollup-freebsd-arm64": "4.60.4", - "@rollup/rollup-freebsd-x64": "4.60.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", - "@rollup/rollup-linux-arm-musleabihf": "4.60.4", - "@rollup/rollup-linux-arm64-gnu": "4.60.4", - "@rollup/rollup-linux-arm64-musl": "4.60.4", - "@rollup/rollup-linux-loong64-gnu": "4.60.4", - "@rollup/rollup-linux-loong64-musl": "4.60.4", - "@rollup/rollup-linux-ppc64-gnu": "4.60.4", - "@rollup/rollup-linux-ppc64-musl": "4.60.4", - "@rollup/rollup-linux-riscv64-gnu": "4.60.4", - "@rollup/rollup-linux-riscv64-musl": "4.60.4", - "@rollup/rollup-linux-s390x-gnu": "4.60.4", - "@rollup/rollup-linux-x64-gnu": "4.60.4", - "@rollup/rollup-linux-x64-musl": "4.60.4", - "@rollup/rollup-openbsd-x64": "4.60.4", - "@rollup/rollup-openharmony-arm64": "4.60.4", - "@rollup/rollup-win32-arm64-msvc": "4.60.4", - "@rollup/rollup-win32-ia32-msvc": "4.60.4", - "@rollup/rollup-win32-x64-gnu": "4.60.4", - "@rollup/rollup-win32-x64-msvc": "4.60.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slice-ansi": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", - "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.3", - "is-fullwidth-code-point": "^5.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-width": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", - "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", - "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "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": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", - "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/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/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - } - } -} From fe08ab1c72e313e6d47c0f06486a6221dc6d0ec2 Mon Sep 17 00:00:00 2001 From: Alexander Kosko Date: Sat, 23 May 2026 10:21:00 +0300 Subject: [PATCH 17/17] chore: ignore package-lock.json --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1a9dbfd..fbfcb98 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ __pycache__/ opencode-plugin-template/ opencode-docs-*/ package-lock.json +package-lock.json