From 326672cb93c8bc98c9943e8cb83c457d81b3d1be Mon Sep 17 00:00:00 2001 From: Wonseok Jung Date: Thu, 30 Apr 2026 00:19:06 +0900 Subject: [PATCH] feat: v2.12.0 - UI/UX Refinement (Model Sync & Premium Tooltips) --- PATCHNOTES.md | 19 + .../Advanced_Features_Implementation_Guide.md | 40 + docs/UX_UI_Consistency_Guidelines.md | 44 + jest.config.js | 9 + package-lock.json | 3814 ++++++++++++++++- package.json | 15 +- src/agent.ts | 607 ++- src/agents/factory.ts | 55 + src/bridge.ts | 3 +- src/config.ts | 184 +- src/core/conflict.ts | 28 + src/core/errorHandler.ts | 41 + src/core/errors.ts | 38 + src/core/health.ts | 59 + src/core/lock.ts | 38 + src/core/queue.ts | 53 + src/core/session.ts | 88 + src/core/statusBar.ts | 57 + src/core/transaction.ts | 127 + src/extension.ts | 16 +- src/sidebarProvider.ts | 396 +- src/utils.ts | 104 +- tests/transaction.test.ts | 68 + tests/vulnerability.test.ts | 60 + tsconfig.json | 6 +- 25 files changed, 5606 insertions(+), 363 deletions(-) create mode 100644 docs/Advanced_Features_Implementation_Guide.md create mode 100644 docs/UX_UI_Consistency_Guidelines.md create mode 100644 jest.config.js create mode 100644 src/agents/factory.ts create mode 100644 src/core/conflict.ts create mode 100644 src/core/errorHandler.ts create mode 100644 src/core/errors.ts create mode 100644 src/core/health.ts create mode 100644 src/core/lock.ts create mode 100644 src/core/queue.ts create mode 100644 src/core/session.ts create mode 100644 src/core/statusBar.ts create mode 100644 src/core/transaction.ts create mode 100644 tests/transaction.test.ts create mode 100644 tests/vulnerability.test.ts diff --git a/PATCHNOTES.md b/PATCHNOTES.md index d299399..fbdfd7d 100644 --- a/PATCHNOTES.md +++ b/PATCHNOTES.md @@ -21,6 +21,25 @@ - Removed premature empty stream chunks that were causing UI flickering. - Verified build stability with `v2.2.46` VSIX package. +--- + +## v2.8.0 (2026-04-29) - Intelligent Collaborative System Evolution + +### ๐Ÿค– Multi-Agent Workflow (MAW) +- **Planner Agent:** ์ „๋žต ์ˆ˜๋ฆฝ ๋ฐ ์ž‘์—… ๋‹จ๊ณ„ ์ž๋™ ์„ค๊ณ„. +- **Researcher Agent:** ์ง€์‹ ๋ฒ ์ด์Šค(Second Brain) ์‹ฌ์ธต ๋ถ„์„ ๋ฐ ๋ฐ์ดํ„ฐ ์ •์ œ. +- **Writer Agent:** ์ตœ์ข… ๋ณด๊ณ ์„œ ํ˜•ํƒœ์˜ ๊ณ ํ’ˆ์งˆ ๋‹ต๋ณ€ ์ƒ์„ฑ ๋ฐ ํ†ตํ•ฉ. +- ๋ณต์žกํ•œ ์š”์ฒญ(๋ถ„์„, ๋ณด๊ณ ์„œ ๋“ฑ) ๊ฐ์ง€ ์‹œ ์ž๋™์œผ๋กœ ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ๋ชจ๋“œ ํ™œ์„ฑํ™”. + +### ๐Ÿ”ฎ Proactive Suggestion Engine +- **Behavioral Tracking:** ์‚ฌ์šฉ์ž UI ์ฒด๋ฅ˜ ์‹œ๊ฐ„(Dwell Time) ๊ธฐ๋ฐ˜ ์˜๋„ ๊ฐ์ง€. +- **Smart Tips:** ์„ค์ •, ์ง€์‹ ๋™๊ธฐํ™”, ์—์ด์ „ํŠธ ์„ ํƒ ๋“ฑ ์ƒํ™ฉ์— ๋งž๋Š” ๋Šฅ๋™์  ๋„์›€๋ง ์ œ๊ณต. + +### ๐ŸŽจ UX & Reliability Improvements +- **Visual Feedback:** ๊ฐ€์ด๋“œ๋ผ์ธ์— ๋”ฐ๋ฅธ ์ƒํƒœ๋ณ„ ์•„์ด์ฝ˜ ๋ฐ ํ”ผ๋“œ๋ฐฑ ์ผ๊ด€์„ฑ ๊ฐ•ํ™”. +- **Model Fix:** ๋ชจ๋ธ ์„ ํƒ ๋ฐ ์ €์žฅ ๋กœ์ง ์•ˆ์ •ํ™” ์™„๋ฃŒ. +- **Agent Handoff:** ๊ฐ ์—์ด์ „ํŠธ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐ ์ง„ํ–‰ ์ƒํ™ฉ ์‹œ๊ฐํ™” ๊ฐœ์„ . + --- *Date: 2026-04-25* *Version: 2.2.46* diff --git a/docs/Advanced_Features_Implementation_Guide.md b/docs/Advanced_Features_Implementation_Guide.md new file mode 100644 index 0000000..d24a162 --- /dev/null +++ b/docs/Advanced_Features_Implementation_Guide.md @@ -0,0 +1,40 @@ +# Advanced Features Implementation Guide + +์ด ๋ฌธ์„œ๋Š” ConnectAI(G1nation)์˜ '์ง€๋Šฅํ˜• ํ˜‘์—… ์‹œ์Šคํ…œ' ๊ตฌํ˜„์„ ์œ„ํ•œ ๊ธฐ์ˆ  ๊ฐ€์ด๋“œ๋ผ์ธ์ž…๋‹ˆ๋‹ค. + +--- + +## ๐Ÿค– 1. ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ์šฐ (Multi-Agent Workflow) + +์‚ฌ์šฉ์ž ์š”์ฒญ์„ ๋ถ„ํ•ด, ๋ถ„์„, ํ†ตํ•ฉํ•˜๋Š” 3๋‹จ๊ณ„ ํ˜‘์—… ์—”์ง„์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. + +### A. ์—์ด์ „ํŠธ ์—ญํ•  ์ •์˜ +- **Planner Agent (์ „๋žต๊ฐ€):** ์š”์ฒญ ๋ถ„์„, ๊ฒ€์ƒ‰ ์ „๋žต ์ˆ˜๋ฆฝ, Task List ์ƒ์„ฑ. +- **Researcher Agent (๋ถ„์„๊ฐ€):** ์ง€์‹ ๋ฒ ์ด์Šค ๋ฐ์ดํ„ฐ ์ถ”์ถœ, ์‹ ๋ขฐ๋„ ํ‰๊ฐ€, ๋ฐ์ดํ„ฐ ์ •์ œ. +- **Writer Agent (ํŽธ์ง‘์ž):** ๋ฐ์ดํ„ฐ ํ†ตํ•ฉ, ํ†ค์•ค๋งค๋„ˆ ์ ์šฉ, ์ตœ์ข… ๋ณด๊ณ ์„œ ์™„์„ฑ. + +### B. ํ•ธ๋“œ์˜คํ”„(Hand-off) ํ๋ฆ„ +1. **Planner:** Thinking Bar ํ™œ์„ฑํ™” ๋ฐ ์ž‘์—… ์„ค๊ณ„๋„ ์ž‘์„ฑ. +2. **Researcher:** ์„ค๊ณ„๋„ ๊ธฐ๋ฐ˜ ์ง€์‹ ๊ฒ€์ƒ‰ ๋ฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ™•๋ณด. +3. **Writer:** ์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋…ผ๋ฆฌ์  ํ๋ฆ„์— ๋”ฐ๋ผ ๊ตฌ์กฐํ™”ํ•˜์—ฌ ์ตœ์ข… ์‘๋‹ต ์ œ๊ณต (์„ฑ๊ณต Toast ์•Œ๋ฆผ). + +--- + +## ๐ŸŒ 2. ๋ชจ๋‹ฌ๋ฆฌํ‹ฐ ํ™•์žฅ ๋ฐ ์„ ์ œ์  ์ œ์•ˆ (The Frontier) + +### A. ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ํŒŒ์ดํ”„๋ผ์ธ +- **์ด๋ฏธ์ง€/PDF:** OCR ๋ฐ ๋ ˆ์ด์•„์›ƒ ๋ถ„์„์„ ํ†ตํ•ด ๊ตฌ์กฐ์  ์ •๋ณด๋ฅผ LLM์— ์ฃผ์ž…. +- **์Œ์„ฑ:** STT ๋ณ€ํ™˜ ๋ฐ ๊ฐ์ • ๋ถ„์„์„ ํ†ตํ•œ ๋‰˜์•™์Šค ์กฐ์ ˆ. + +### B. ์„ ์ œ์  ์ œ์•ˆ (Proactive Suggestion) +- **ํ–‰๋™ ๊ฐ์ง€:** ์‚ฌ์šฉ์ž์˜ ํƒ์ƒ‰ ํŒจํ„ด(ํŠน์ • ์ปดํฌ๋„ŒํŠธ ์ฒด๋ฅ˜ ์‹œ๊ฐ„ ๋“ฑ)์„ ์ด๋ฒคํŠธ๋กœ ํฌ์ฐฉ. +- **์˜ˆ์ธก ์ œ์•ˆ:** ํ–‰๋™ ํŒจํ„ด ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ํ•„์š”๋กœ ํ•  ๊ธฐ๋Šฅ์„ ๋ฏธ๋ฆฌ ์ œ์‹œ (์˜ˆ: ๐Ÿ’ก ํšจ์œจ ์ฆ๋Œ€ ์„ค์ • ์ œ์•ˆ). + +--- + +## โœ… ํ†ตํ•ฉ ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- [ ] FSD ๊ธฐ๋ฐ˜ ๋ชจ๋“ˆ ๋ถ„๋ฆฌ +- [ ] Planner -> Researcher -> Writer ํ•ธ๋“œ์˜คํ”„ ๋กœ์ง +- [ ] ์ด๋ฏธ์ง€/PDF ๋ ˆ์ด์•„์›ƒ ๋ถ„์„ ๋ชจ๋“ˆ ์—ฐ๋™ +- [ ] ์‚ฌ์šฉ์ž ํ–‰๋™ ํŒจํ„ด ๊ฐ์ง€๊ธฐ ๊ตฌํ˜„ +- [ ] ์ผ๊ด€๋œ UX ํ”ผ๋“œ๋ฐฑ(Toast, Thinking Bar) ์ ์šฉ diff --git a/docs/UX_UI_Consistency_Guidelines.md b/docs/UX_UI_Consistency_Guidelines.md new file mode 100644 index 0000000..dcc9f47 --- /dev/null +++ b/docs/UX_UI_Consistency_Guidelines.md @@ -0,0 +1,44 @@ +# UX/UI Consistency Guidelines + +์ด ๋ฌธ์„œ๋Š” ConnectAI(G1nation) ํ”„๋กœ์ ํŠธ์˜ UI ์ผ๊ด€์„ฑ๊ณผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ํ•ต์‹ฌ ๊ทœ์น™์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์‹ ๊ทœ ๊ธฐ๋Šฅ ๋ฐ UI ์ˆ˜์ •์€ ์ด ๊ฐ€์ด๋“œ๋ผ์ธ์„ ์ค€์ˆ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +--- + +## ๐ŸŽจ 1. ์ƒํ˜ธ์ž‘์šฉ ํŒจํ„ด (Interaction Pattern) + +### Rule A: ์ƒ์„ฑ ๋ฐ ์ถ”๊ฐ€ (Create/Add) +* **์‹œ๊ฐ์  ๋ฌธ๋ฒ•:** ํ•ต์‹ฌ ์ƒ์„ฑ ์•ก์…˜์€ `+` ์•„์ด์ฝ˜์„ ํฌํ•จํ•œ ๋ฒ„ํŠผ ํ˜•ํƒœ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. +* **๋ฐฐ์น˜ ๊ทœ์น™:** + * **์„น์…˜ ๋ ˆ๋ฒจ:** ์„น์…˜ ์ „์ฒด์— ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ๊ฒฝ์šฐ ์šฐ์ธก ์ƒ๋‹จ ๋˜๋Š” ์šฐ์ธก ํ•˜๋‹จ์— ๋ช…ํ™•ํ•œ ๋ฒ„ํŠผ์œผ๋กœ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. + * **์ฝ˜ํ…์ธ  ๋ ˆ๋ฒจ:** ํŠน์ • ์•„์ดํ…œ ๋‚ด ์ถ”๊ฐ€๋Š” ์•„์ดํ…œ ์šฐ์ธก ์ƒ๋‹จ์— ์ž‘์€ `+` ์•„์ด์ฝ˜ ๋ฒ„ํŠผ์œผ๋กœ ๊ณ ์ •ํ•ฉ๋‹ˆ๋‹ค. + +### Rule B: ํŽธ์ง‘ ๋ฐ ์ˆ˜์ • (Edit/Modify) +* **ํŽธ์ง‘ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ์•ˆ๋‚ด:** ์ˆ˜์ •์ด ๋ถˆ๊ฐ€๋Šฅํ•œ 'Source of Truth' ๋ฐ์ดํ„ฐ๋Š” ํŽธ์ง‘ ๋ฒ„ํŠผ์„ ๋…ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉฐ, ํ•„์š” ์‹œ ํˆดํŒ์œผ๋กœ ์‚ฌ์œ ๋ฅผ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค. +* **์ˆ˜์ • ๋ฐฉ์‹:** ํŽธ์ง‘ ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ณตํ†ต์ ์œผ๋กœ `โš™๏ธ` (์„ค์ •) ๋˜๋Š” `โ‹ฎ` (๋”๋ณด๊ธฐ) ์•„์ด์ฝ˜์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋ชจ๋‹ฌ(Modal) ์ฐฝ ๋˜๋Š” ์‚ฌ์ด๋“œ ํŒจ๋„ ๋ฐฉ์‹์„ ํ†ตํ•ด ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค. + +--- + +## ๐Ÿ–Œ๏ธ 2. ๋น„์ฃผ์–ผ ๊ฐ€์ด๋“œ๋ผ์ธ (Visual Guidelines) + +### A. ์•„์ด์ฝ”๋…ธ๊ทธ๋ž˜ํ”ผ (Iconography) ํ†ต์ผ +* **์ถ”๊ฐ€:** `+` (Plus) +* **ํŽธ์ง‘/์„ค์ •:** `โš™๏ธ` (Gear) ๋˜๋Š” `โ‹ฎ` (Vertical Ellipsis) +* **์‚ญ์ œ:** `๐Ÿ—‘๏ธ` (Trash) +* **๋™๊ธฐํ™”:** `โ†ป` (Refresh) + +### B. ์ •๋ณด ๊ณ„์ธต ๊ตฌ์กฐ (Information Hierarchy) +* **ํ•ต์‹ฌ ์•ก์…˜ ์šฐ์„ :** ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์žฅ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ(์งˆ๋ฌธ ์ž…๋ ฅ, ์ฑ„ํŒ… ์‹œ์ž‘)์ด ์‹œ๊ฐ์ ์œผ๋กœ ๊ฐ€์žฅ ๊ฐ•์กฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +* **๊ทธ๋ฃนํ™”:** ๊ด€๋ จ ์žˆ๋Š” ๊ธฐ๋Šฅ(AI ์—”์ง„ ์„ค์ •, ์ง€์‹ ๋ฒ ์ด์Šค ๊ด€๋ฆฌ, ๋„๊ตฌ ๋ชจ์Œ)์€ ์‹œ๊ฐ์ ์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ์ธ์ง€ ๋ถ€ํ•˜๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค. + +### C. ํ”ผ๋“œ๋ฐฑ ๋ฐ ์ƒํƒœ ๋ฉ”์‹œ์ง€ (Feedback & State) +* **์ƒ‰์ƒ ์ฝ”๋“œ:** + * ๐ŸŸข **์„ฑ๊ณต:** ๋…น์ƒ‰ ๊ณ„์—ด (#00FF41) - Toast ์•Œ๋ฆผ ์‚ฌ์šฉ + * ๐Ÿ”ด **์˜ค๋ฅ˜:** ๋นจ๊ฐ„์ƒ‰ ๊ณ„์—ด (#ff5252) - ์—๋Ÿฌ ๋ชจ๋‹ฌ ๋˜๋Š” ๋ช…ํ™•ํ•œ ๋ฐฐ๋„ˆ ์‚ฌ์šฉ + * ๐ŸŸก **์ •๋ณด/์ง„ํ–‰:** ํŒŒ๋ž€์ƒ‰/๋ณด๋ผ์ƒ‰ ๊ณ„์—ด - Thinking Bar ๋ฐ ๋กœ๋”ฉ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‚ฌ์šฉ + +--- + +## ๐Ÿš€ 3. ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- [ ] ์ƒˆ๋กœ์šด ๋ฒ„ํŠผ์ด ๊ธฐ์กด์˜ ์•„์ด์ฝ˜ ๋ฌธ๋ฒ•์„ ๋”ฐ๋ฅด๋Š”๊ฐ€? +- [ ] ์ƒ์„ฑ/ํŽธ์ง‘ ์•ก์…˜์ด ์ •์˜๋œ ์œ„์น˜์— ๋ฐฐ์น˜๋˜์—ˆ๋Š”๊ฐ€? +- [ ] ์ƒํƒœ ๋ณ€ํ™”์— ๋Œ€ํ•ด ์ผ๊ด€๋œ ์ƒ‰์ƒ๊ณผ ๋ฐฉ์‹์œผ๋กœ ํ”ผ๋“œ๋ฐฑ์„ ์ฃผ๋Š”๊ฐ€? diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..145d7ac --- /dev/null +++ b/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/tests/**/*.test.ts'], + moduleFileExtensions: ['ts', 'js'], + transform: { + '^.+\\.ts$': 'ts-jest', + }, +}; diff --git a/package-lock.json b/package-lock.json index a34088a..78ad1fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,28 +1,527 @@ { "name": "g1nation", - "version": "2.2.63", + "version": "2.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "g1nation", - "version": "2.2.63", + "version": "2.3.1", "license": "MIT", "dependencies": { "marked": "^18.0.2" }, "devDependencies": { + "@types/jest": "^29.5.14", "@types/marked": "^5.0.2", "@types/node": "18.x", "@types/vscode": "^1.80.0", "@vercel/ncc": "^0.38.4", "esbuild": "^0.28.0", + "jest": "^29.7.0", + "ts-jest": "^29.4.9", "typescript": "^5.1.3" }, "engines": { "vscode": "^1.80.0" } }, + "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/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "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/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", @@ -465,6 +964,495 @@ "node": ">=18" } }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.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/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/marked": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", @@ -482,6 +1470,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.115.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.115.0.tgz", @@ -489,6 +1484,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@vercel/ncc": { "version": "0.38.4", "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.4.tgz", @@ -499,6 +1511,601 @@ "ncc": "dist/ncc/cli.js" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.24.tgz", + "integrity": "sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "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/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "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/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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/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-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", @@ -541,6 +2148,1259 @@ "@esbuild/win32-x64": "0.28.0" } }, + "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/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.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-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "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-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, "node_modules/marked": { "version": "18.0.2", "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.2.tgz", @@ -553,6 +3413,765 @@ "node": ">= 20" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "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/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "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/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/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/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.9", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", + "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.4", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -567,12 +4186,203 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true, "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "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/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 2c84fb1..302176b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "g1nation", "displayName": "G1nation", "description": "100% local AI coding agent for VS Code. Create files, edit code, run commands, and work offline with Ollama or LM Studio.", - "version": "2.2.67", + "version": "2.12.0", "publisher": "connectailab", "license": "MIT", "icon": "assets/icon.png", @@ -95,6 +95,11 @@ "configuration": { "title": "G1nation", "properties": { + "g1nation.multiAgentEnabled": { + "type": "boolean", + "default": true, + "description": "Enable Multi-Agent Workflow (Planner -> Researcher -> Writer) for complex tasks." + }, "g1nation.ollamaUrl": { "type": "string", "default": "http://127.0.0.1:11434", @@ -169,6 +174,11 @@ "type": "number", "default": 50, "description": "Maximum autonomous steps the agent can take per request. Default: 50" + }, + "g1nation.dryRun": { + "type": "boolean", + "default": false, + "description": "If enabled, the agent will ask for approval before committing any file changes." } } } @@ -180,11 +190,14 @@ "pretest": "npm run compile" }, "devDependencies": { + "@types/jest": "^29.5.14", "@types/marked": "^5.0.2", "@types/node": "18.x", "@types/vscode": "^1.80.0", "@vercel/ncc": "^0.38.4", "esbuild": "^0.28.0", + "jest": "^29.7.0", + "ts-jest": "^29.4.9", "typescript": "^5.1.3" }, "dependencies": { diff --git a/src/agent.ts b/src/agent.ts index b659bc5..42a53fb 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -3,10 +3,8 @@ import * as path from 'path'; import * as fs from 'fs'; // axios removed import { - getConfig, _getBrainDir, findBrainFiles, - EXCLUDED_DIRS, getSystemPrompt, shouldAutoPushBrain, getSecondBrainRepo, @@ -17,16 +15,53 @@ import { resolveEngine, summarizeText } from './utils'; +import { getConfig, EXCLUDED_DIRS } from './config'; import { validatePath, sanitizeCommand } from './security'; +import { TransactionManager } from './core/transaction'; +import { SessionManager } from './core/session'; +import { PlannerAgent, ResearcherAgent, WriterAgent } from './agents/factory'; +import { ErrorTranslator } from './core/errorHandler'; +import { + AgentExecutionError, + FileSystemError, + APICommunicationError +} from './core/errors'; +import { StatusBarManager, AgentStatus } from './core/statusBar'; +import { lockManager } from './core/lock'; +import { actionQueue } from './core/queue'; +import { ConflictResolver } from './core/conflict'; export interface ChatMessage { role: 'user' | 'assistant' | 'system'; - content: string | any[]; + content: string; internal?: boolean; + rationale?: { + problem: string; + goal: string; + reasoning: string; + }; } type HistoryChangeListener = (history: ChatMessage[]) => void | Promise; +// --- Agent Roles & Workflows --- +export type AgentRole = 'planner' | 'researcher' | 'writer'; + +const AGENT_PROMPTS: Record = { + planner: `You are the [Planner Agent]. Your goal is to analyze the user's request and create a detailed execution plan. +1. Breakdown the request into logical steps. +2. Identify key search keywords for the knowledge base. +3. Output your plan in a structured format using tags.`, + researcher: `You are the [Researcher Agent]. Your goal is to gather and analyze data based on the Planner's strategy. +1. Search the local knowledge base using the provided keywords. +2. Evaluate data reliability and extract relevant facts. +3. Output your findings using tags.`, + writer: `You are the [Writer Agent]. Your goal is to synthesize all gathered information into a high-quality final report. +1. Use the data from the Researcher. +2. Follow the project's visual and tone-of-voice guidelines. +3. Deliver a logical, consistent, and polished response.` +}; + export class AgentExecutor { private chatHistory: ChatMessage[] = []; private abortController: AbortController | null = null; @@ -34,10 +69,44 @@ export class AgentExecutor { private historyChangeListener: HistoryChangeListener | undefined; private runSerial = 0; private activeRunId = 0; + private transactionManager: TransactionManager; + private sessionManager: SessionManager; + private statusBarManager: StatusBarManager; + private currentTaskId: string = 'default_session'; constructor( private context: vscode.ExtensionContext - ) {} + ) { + this.transactionManager = new TransactionManager(); + this.sessionManager = new SessionManager(this.context); + this.statusBarManager = new StatusBarManager(); + this.restoreLastSession(); + } + + private parseRationale(text: string) { + const match = text.match(/([\s\S]*?)<\/rationale>/); + if (!match) return undefined; + + const raw = match[1]; + const problem = raw.match(/\[PROBLEM\]([\s\S]*?)(?=\[|$)/)?.[1]?.trim() || ""; + const goal = raw.match(/\[GOAL\]([\s\S]*?)(?=\[|$)/)?.[1]?.trim() || ""; + const reasoning = raw.match(/\[REASONING\]([\s\S]*?)(?=\[|$)/)?.[1]?.trim() || raw.trim(); + + return { problem, goal, reasoning }; + } + + private async restoreLastSession() { + try { + const lastSession = this.sessionManager.loadLastActiveSession(); + if (lastSession) { + this.chatHistory = lastSession.history; + this.currentTaskId = lastSession.taskId; + logInfo(`Restored last session: ${this.currentTaskId}`); + } + } catch (error) { + logError('Failed to restore last session. Starting fresh.', error); + } + } public setWebview(webview: vscode.Webview) { this.webview = webview; @@ -75,6 +144,20 @@ export class AgentExecutor { this.emitHistoryChanged(); } + public async approveTransaction() { + if (!this.transactionManager.isActive()) return; + this.transactionManager.commit(); + this.statusBarManager.updateStatus(AgentStatus.Success, 'Changes committed.'); + this.webview?.postMessage({ type: 'streamChunk', value: '\nโœ… **์ž‘์—…์ด ์Šน์ธ๋˜์–ด ๋ฐ˜์˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค.**' }); + } + + public async rejectTransaction() { + if (!this.transactionManager.isActive()) return; + this.transactionManager.rollback(); + this.statusBarManager.updateStatus(AgentStatus.Idle, 'Changes rolled back.'); + this.webview?.postMessage({ type: 'streamChunk', value: '\nโŒ **์ž‘์—…์ด ๊ฑฐ๋ถ€๋˜์–ด ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.**' }); + } + public async handlePrompt( prompt: string | null, modelName: string, @@ -98,19 +181,35 @@ export class AgentExecutor { temperature = 0.7, systemPrompt = getSystemPrompt() } = options; + const { ollamaUrl, defaultModel: configDefaultModel, timeout, multiAgentEnabled } = getConfig(); const runId = options.runId ?? (loopDepth === 0 ? ++this.runSerial : this.activeRunId); + + // Decide whether to use Multi-Agent Workflow (for complex requests) + const isComplex = prompt && (prompt.length > 100 || /(๋ถ„์„|๋ณด๊ณ ์„œ|์„ค๊ณ„|๊ธฐํš|์ •๋ฆฌ)/.test(prompt)); + if (isComplex && loopDepth === 0 && multiAgentEnabled) { + return this.executeMultiAgentWorkflow(prompt!, modelName, options); + } + const hasVisionContent = Array.isArray(visionContent) ? visionContent.length > 0 : !!visionContent; let requestTimeoutHandle: ReturnType | undefined; if (!this.webview) return; try { + // 0. Safety Check: Rollback any dangling transaction from previous runs + if (this.transactionManager.isActive()) { + logInfo('Cleaning up dangling transaction from previous session.'); + this.transactionManager.rollback(); + } + + this.statusBarManager.updateStatus(AgentStatus.Thinking); if (loopDepth === 0) { if (this.abortController) { this.abortController.abort(); this.abortController = null; } this.activeRunId = runId; + this.currentTaskId = `task_${Date.now()}`; await this.context.workspaceState.update('lastActionStr', undefined); } @@ -170,7 +269,8 @@ export class AgentExecutor { } // 3. API Request Setup - const { ollamaUrl, defaultModel, timeout } = getConfig(); + const { ollamaUrl, defaultModel: configDefaultModel, timeout } = getConfig(); + const actualModel = modelName || configDefaultModel; const reqMessages = [...this.chatHistory]; // Handle Vision Content Injection @@ -184,7 +284,7 @@ export class AgentExecutor { : []; reqMessages[lastUserIdx] = { role: 'user', - content: [...textParts, ...(visionContent || [])] + content: JSON.stringify([...textParts, ...(visionContent || [])]) }; } } @@ -208,12 +308,12 @@ export class AgentExecutor { // 4. Call AI Engine this.abortController = new AbortController(); requestTimeoutHandle = setTimeout(() => { - logError('AI request timed out.', { timeoutMs: timeout, model: modelName || defaultModel, loopDepth }); + logError('AI request timed out.', { timeoutMs: timeout, model: actualModel, loopDepth }); this.abortController?.abort(); }, timeout); const request = await this.createStreamingRequest({ baseUrl: ollamaUrl, - modelName: modelName || defaultModel, + modelName: actualModel, reqMessages: messagesForRequest, temperature }); @@ -283,12 +383,15 @@ export class AgentExecutor { } // 5. Execute Actions - const assistantMessage: ChatMessage = { role: 'assistant', content: aiResponseText, internal: true }; + const rationale = this.parseRationale(aiResponseText); + const assistantMessage: ChatMessage = { role: 'assistant', content: aiResponseText, internal: true, rationale }; this.chatHistory.push(assistantMessage); + + this.statusBarManager.updateStatus(AgentStatus.Executing); const report = await this.executeActions(aiResponseText, rootPath); if (!aiResponseText.trim() && report.length === 0) { this.chatHistory.pop(); - logError('Model returned an empty response without actions.', { model: modelName || defaultModel, loopDepth }); + logError('Model returned an empty response without actions.', { model: actualModel, loopDepth }); this.webview.postMessage({ type: 'error', value: 'AI model returned an empty response.' }); return; } @@ -332,9 +435,11 @@ export class AgentExecutor { assistantMessage.internal = false; this.emitHistoryChanged(); + this.statusBarManager.updateStatus(AgentStatus.Success); this.webview.postMessage({ type: 'streamChunk', value: aiResponseText }); } catch (error: any) { + this.statusBarManager.updateStatus(AgentStatus.Error, error.message); logError('Agent prompt failed.', { error: error?.message || String(error), promptPreview: summarizeText(prompt || '', 200) }); if (!this.isStaleRun(runId)) { this.webview.postMessage({ type: "error", value: `[Agent Error]: ${error.message}` }); @@ -383,16 +488,15 @@ export class AgentExecutor { if (explicitPath) return explicitPath; const namedProject = prompt.match(/([A-Za-z0-9._-]+)\s*(?:ํ”„๋กœ์ ํŠธ|project)/i)?.[1]; - if (!namedProject) return null; + if (!namedProject) return null; // No project keyword found, do not attempt to guess. const searchRoots = [ + vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '', '/Volumes/Data/project/Antigravity', - '/Volumes/Data/project', - vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '' ].filter(Boolean); for (const root of searchRoots) { - const resolved = this.findDirectoryByName(root, namedProject, 4); + const resolved = this.findDirectoryByName(root, namedProject, 2); // Depth reduced to 2 for performance and accuracy. if (resolved) return resolved; } @@ -424,10 +528,108 @@ export class AgentExecutor { return null; } + public async executeMultiAgentWorkflow( + prompt: string, + modelName: string, + options: any + ) { + if (!this.webview) return; + this.statusBarManager.updateStatus(AgentStatus.Thinking, 'Multi-Agent Workflow Started'); + this.webview.postMessage({ type: 'streamStart' }); + + try { + // Instantiate decoupled agents + const planner = new PlannerAgent(modelName); + const researcher = new ResearcherAgent(modelName); + const writer = new WriterAgent(modelName); + + // Prepare Context + const activeBrain = getActiveBrainProfile(); + const brainFiles = findBrainFiles(activeBrain.localBrainPath); + const brainContext = `Brain: ${activeBrain.name}, Files: ${brainFiles.length}`; + + // --- Phase 1: Planner --- + this.webview.postMessage({ type: 'autoContinue', value: 'Planner: ์ „๋žต ์ˆ˜๋ฆฝ ์ค‘...' }); + const plan = await planner.execute(prompt, brainContext); + this.webview.postMessage({ type: 'streamChunk', value: `\n\n### ๐Ÿ“ ์ž‘์—… ๊ณ„ํš (Execution Plan)\n${plan}\n\n` }); + + // --- Phase 2: Researcher --- + this.webview.postMessage({ type: 'autoContinue', value: 'Researcher: ์ง€์‹ ๊ฒ€์ƒ‰ ์ค‘...' }); + const research = await researcher.execute(plan, brainContext); + this.webview.postMessage({ type: 'streamChunk', value: `\n\n### ๐Ÿ” ๋ถ„์„ ๊ฒฐ๊ณผ (Research Findings)\n*(์ •๋ณด ์ˆ˜์ง‘ ๋ฐ ์ •์ œ ์™„๋ฃŒ)*\n\n` }); + + // --- Phase 3: Writer --- + this.webview.postMessage({ type: 'autoContinue', value: 'Writer: ๋ณด๊ณ ์„œ ์ž‘์„ฑ ์ค‘...' }); + const finalReport = await writer.execute(research, prompt); + + this.webview.postMessage({ type: 'streamChunk', value: `\n\n--- \n\n${finalReport}` }); + this.webview.postMessage({ type: 'streamEnd' }); + + this.chatHistory.push({ role: 'assistant', content: finalReport }); + this.emitHistoryChanged(); + + this.statusBarManager.updateStatus(AgentStatus.Success, 'Workflow Complete'); + this.webview.postMessage({ type: 'autoContinue', value: 'โœ… ๋ถ„์„์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!' }); + + } catch (error: any) { + const friendly = ErrorTranslator.translate(error); + logError('Workflow failed', error); + + // Format error using guideline-compliant UI (Red color scheme) + this.webview.postMessage({ + type: 'error', + value: `### ${friendly.title}\n\n**์ƒํƒœ:** ${friendly.message}\n\n**ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:** ${friendly.action}` + }); + this.statusBarManager.updateStatus(AgentStatus.Idle, 'Error occurred'); + } + } + + private async callAgent(role: AgentRole, prompt: string, modelName: string, options: any): Promise { + const persona = AGENT_PROMPTS[role]; + const { ollamaUrl, timeout } = getConfig(); + + const messages: ChatMessage[] = [ + { role: 'system', content: persona }, + { role: 'user', content: prompt } + ]; + + const request = await this.createStreamingRequest({ + baseUrl: ollamaUrl, + modelName: modelName, + reqMessages: messages, + temperature: 0.3 // Use lower temperature for planning and research + }); + + let responseText = ''; + const reader = request.response.body?.getReader(); + if (!reader) throw new Error("Agent response body is not readable."); + + const decoder = new TextDecoder(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + const chunk = decoder.decode(value, { stream: true }); + const lines = chunk.split('\n'); + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed === 'data: [DONE]') continue; + try { + const json = JSON.parse(trimmed.startsWith('data: ') ? trimmed.slice(6) : trimmed); + const content = json.choices?.[0]?.delta?.content || json.message?.content || ''; + responseText += content; + } catch (e) {} + } + } + return responseText; + } + private isProjectAnalysisRequest(normalized: string): boolean { - // Intentionally conservative: omit generic words like '์กฐ์‚ฌ', '์„ค๋ช…', 'ํŒŒ์•…' - // that appear in ordinary chat and would incorrectly bypass LM Studio - return /(๋ถ„์„|๋ฆฌ๋ทฐ|์„ค๊ณ„|๊ตฌ์กฐ|์–ด๋–ค ํ”„๋กœ๊ทธ๋žจ|๋ฌด์Šจ ํ”„๋กœ๊ทธ๋žจ|์ œํ’ˆ ์„ค๋ช…)/.test(normalized); + // Only trigger local analysis if the intent is strictly about a project-level overview. + // Avoid generic terms like 'analysis' or 'review' that are common in general coding chat. + const hasProjectKeyword = /(ํ”„๋กœ์ ํŠธ|project|๋ ˆํฌ|repository)/.test(normalized); + const hasAnalysisIntent = /(์ „์ฒด ์š”์•ฝ|์ œํ’ˆ ์„ค๋ช…|์–ด๋–ค ํ”„๋กœ๊ทธ๋žจ|๊ตฌ์กฐ ํŒŒ์•…|ํ›‘์–ด๋ณด๊ธฐ)/.test(normalized); + + return hasProjectKeyword && hasAnalysisIntent; } private buildProjectAnalysisReply(projectPath: string): string { @@ -691,6 +893,13 @@ export class AgentExecutor { private emitHistoryChanged() { if (!this.historyChangeListener) return; + // Save session whenever history changes + this.sessionManager.saveSession( + this.currentTaskId, + this.chatHistory, + this.context.workspaceState.get('lastActionStr') + ); + Promise.resolve(this.historyChangeListener(this.getHistory())).catch((error: any) => { logError('History change listener failed.', { error: error?.message || String(error) }); }); @@ -818,179 +1027,219 @@ export class AgentExecutor { let brainModified = false; let firstCreatedFile: string | undefined; - // Action 1: Create File - const createRegex = /([\s\S]*?)<\/create_file>/gi; - let match; - while ((match = createRegex.exec(aiMessage)) !== null) { - const relPath = match[1].trim(); - const content = match[2].trim(); - try { - const absPath = validatePath(rootPath, relPath); - fs.mkdirSync(path.dirname(absPath), { recursive: true }); - fs.writeFileSync(absPath, content, 'utf-8'); - report.push(`โœ… Created: ${relPath}`); - if (!firstCreatedFile) firstCreatedFile = absPath; - if (absPath.startsWith(_getBrainDir())) brainModified = true; - } catch (err: any) { report.push(`โŒ Error Creating ${relPath}: ${err.message}`); } - } + try { + this.transactionManager.begin(); - // Action 2: Edit File - const editRegex = /([\s\S]*?)<\/edit_file>/gi; - while ((match = editRegex.exec(aiMessage)) !== null) { - const relPath = match[1].trim(); - const editContent = match[2].trim(); - try { - const absPath = validatePath(rootPath, relPath); - if (fs.existsSync(absPath)) { - let currentContent = fs.readFileSync(absPath, 'utf-8'); - const searchMatch = editContent.match(/([\s\S]*?)<\/search>\s*([\s\S]*?)<\/replace>/i); - if (searchMatch) { - const searchStr = searchMatch[1]; - const replaceStr = searchMatch[2]; - if (currentContent.includes(searchStr)) { - currentContent = currentContent.replace(searchStr, replaceStr); - fs.writeFileSync(absPath, currentContent, 'utf-8'); - report.push(`๐Ÿ“ Updated: ${relPath}`); - } else { - report.push(`โš ๏ธ Search string not found in ${relPath}`); - } - } else { - fs.writeFileSync(absPath, editContent, 'utf-8'); - report.push(`๐Ÿ“ Updated (Full): ${relPath}`); - } + // Action 1: Create File + const createRegex = /([\s\S]*?)<\/create_file>/gi; + let match; + while ((match = createRegex.exec(aiMessage)) !== null) { + const relPath = match[1].trim(); + const content = match[2].trim(); + try { + const absPath = validatePath(rootPath, relPath); + await this.transactionManager.record(absPath); + + fs.mkdirSync(path.dirname(absPath), { recursive: true }); + fs.writeFileSync(absPath, content, 'utf-8'); + + report.push(`โœ… Created: ${relPath}`); + if (!firstCreatedFile) firstCreatedFile = absPath; if (absPath.startsWith(_getBrainDir())) brainModified = true; - } else { - report.push(`โŒ File not found: ${relPath}`); + } catch (err: any) { + throw new FileSystemError(`Failed to create file ${relPath}: ${err.message}`, relPath, err); } - } catch (err: any) { report.push(`โŒ Error Editing ${relPath}: ${err.message}`); } - } + } - // Action 3: Delete File - const deleteRegex = /(?:<\/delete_file>)?/gi; - while ((match = deleteRegex.exec(aiMessage)) !== null) { - const relPath = match[1].trim(); - try { - const absPath = validatePath(rootPath, relPath); - if (fs.existsSync(absPath)) { - fs.unlinkSync(absPath); - report.push(`๐Ÿ—‘ Deleted: ${relPath}`); - } else { - report.push(`โš ๏ธ Delete failed: ${relPath} not found`); - } - } catch (err: any) { report.push(`โŒ Error Deleting ${relPath}: ${err.message}`); } - } - - // Action 4: Read File - const readRegex = /(?:<\/read_file>)?/gi; - while ((match = readRegex.exec(aiMessage)) !== null) { - const relPath = match[1].trim(); - try { - const absPath = validatePath(rootPath, relPath); - if (fs.existsSync(absPath)) { - const content = fs.readFileSync(absPath, 'utf-8'); - const preview = content.length > 8000 ? content.slice(0, 8000) + "\n... (truncated)" : content; - report.push(`๐Ÿ“– Read: ${relPath}`); - this.chatHistory.push({ role: 'system', content: `[Result of read_file ${relPath}]\n\`\`\`\n${preview}\n\`\`\``, internal: true }); - } else { - report.push(`โŒ Read failed: ${relPath} not found`); - } - } catch (err: any) { report.push(`โŒ Error Reading ${relPath}: ${err.message}`); } - } - - // Action 5: Run Command - const cmdRegex = /([\s\S]*?)<\/run_command>/gi; - while ((match = cmdRegex.exec(aiMessage)) !== null) { - const cmd = match[1].trim(); - try { - const safeCmd = sanitizeCommand(cmd); - const terminal = vscode.window.terminals.find(t => t.name === 'G1nation Terminal') || vscode.window.createTerminal({ name: 'G1nation Terminal', cwd: rootPath }); - terminal.show(); - terminal.sendText(safeCmd); - report.push(`๐Ÿš€ Executed: ${safeCmd}`); - } catch (err: any) { report.push(`โŒ Blocked: ${err.message}`); } - } - - // Action 6: List Files - const listRegex = /(?:<\/list_files>)?/gi; - while ((match = listRegex.exec(aiMessage)) !== null) { - const relPath = match[1].trim() || '.'; - try { - const absPath = validatePath(rootPath, relPath); - if (fs.existsSync(absPath) && fs.statSync(absPath).isDirectory()) { - const entries = fs.readdirSync(absPath, { withFileTypes: true }); - let listing = entries - .filter(e => !e.name.startsWith('.') && !EXCLUDED_DIRS.has(e.name)) - .map(e => e.isDirectory() ? `${e.name}/` : e.name) - .join('\n'); - - if (listing.length > 5000) { - listing = listing.slice(0, 5000) + "\n... (truncated for context)"; + // Action 2: Edit File + const editRegex = /([\s\S]*?)<\/edit_file>/gi; + while ((match = editRegex.exec(aiMessage)) !== null) { + const relPath = match[1].trim(); + const editContent = match[2].trim(); + try { + const absPath = validatePath(rootPath, relPath); + if (fs.existsSync(absPath)) { + await this.transactionManager.record(absPath); + + let currentContent = fs.readFileSync(absPath, 'utf-8'); + const searchMatch = editContent.match(/([\s\S]*?)<\/search>\s*([\s\S]*?)<\/replace>/i); + + if (searchMatch) { + const searchStr = searchMatch[1]; + const replaceStr = searchMatch[2]; + if (currentContent.includes(searchStr)) { + currentContent = currentContent.replace(searchStr, replaceStr); + fs.writeFileSync(absPath, currentContent, 'utf-8'); + report.push(`๐Ÿ“ Updated: ${relPath}`); + } else { + report.push(`โš ๏ธ Search string not found in ${relPath}`); + } + } else { + fs.writeFileSync(absPath, editContent, 'utf-8'); + report.push(`๐Ÿ“ Updated (Full): ${relPath}`); + } + if (absPath.startsWith(_getBrainDir())) brainModified = true; + } else { + report.push(`โŒ File not found: ${relPath}`); } - - report.push(`๐Ÿ“‚ Listed: ${relPath}`); - this.chatHistory.push({ role: 'system', content: `[Result of list_files ${relPath}]\n${listing}`, internal: true }); + } catch (err: any) { + throw new FileSystemError(`Failed to edit file ${relPath}: ${err.message}`, relPath, err); } - } catch (err: any) { report.push(`โŒ Listing failed: ${err.message}`); } - } + } - // Action 7: Second Brain Knowledge (List/Read) - const listBrainRegex = /(?:<\/list_brain>)?/gi; - while ((match = listBrainRegex.exec(aiMessage)) !== null) { - const relPath = match[1].trim() || '.'; - try { - const brainDir = _getBrainDir(); - const absPath = path.join(brainDir, relPath); - if (fs.existsSync(absPath) && fs.statSync(absPath).isDirectory()) { - const entries = fs.readdirSync(absPath, { withFileTypes: true }); - let listing = entries - .filter(e => !e.name.startsWith('.') && !EXCLUDED_DIRS.has(e.name)) - .map(e => e.isDirectory() ? `${e.name}/` : e.name) - .join('\n'); - - if (listing.length > 5000) { - listing = listing.slice(0, 5000) + "\n... (truncated for context)"; + // Action 3: Delete File + const deleteRegex = /(?:<\/delete_file>)?/gi; + while ((match = deleteRegex.exec(aiMessage)) !== null) { + const relPath = match[1].trim(); + try { + const absPath = validatePath(rootPath, relPath); + if (fs.existsSync(absPath)) { + await this.transactionManager.record(absPath); + fs.unlinkSync(absPath); + report.push(`๐Ÿ—‘ Deleted: ${relPath}`); + } else { + report.push(`โš ๏ธ Delete failed: ${relPath} not found`); } + } catch (err: any) { + throw new FileSystemError(`Failed to delete file ${relPath}: ${err.message}`, relPath, err); + } + } + + // Action 4: Read File (Non-state-changing, no transaction record needed) + const readRegex = /(?:<\/read_file>)?/gi; + while ((match = readRegex.exec(aiMessage)) !== null) { + const relPath = match[1].trim(); + try { + const absPath = validatePath(rootPath, relPath); + if (fs.existsSync(absPath)) { + const content = fs.readFileSync(absPath, 'utf-8'); + const preview = content.length > 8000 ? content.slice(0, 8000) + "\n... (truncated)" : content; + report.push(`๐Ÿ“– Read: ${relPath}`); + this.chatHistory.push({ role: 'system', content: `[Result of read_file ${relPath}]\n\`\`\`\n${preview}\n\`\`\``, internal: true }); + } else { + report.push(`โŒ Read failed: ${relPath} not found`); + } + } catch (err: any) { report.push(`โŒ Error Reading ${relPath}: ${err.message}`); } + } + + // Action 5: Run Command + const cmdRegex = /([\s\S]*?)<\/run_command>/gi; + while ((match = cmdRegex.exec(aiMessage)) !== null) { + const cmd = match[1].trim(); + try { + const safeCmd = sanitizeCommand(cmd); + const terminal = vscode.window.terminals.find(t => t.name === 'G1nation Terminal') || vscode.window.createTerminal({ name: 'G1nation Terminal', cwd: rootPath }); + terminal.show(); + terminal.sendText(safeCmd); + report.push(`๐Ÿš€ Executed: ${safeCmd}`); + } catch (err: any) { report.push(`โŒ Blocked: ${err.message}`); } + } + + // Action 6: List Files + const listRegex = /(?:<\/list_files>)?/gi; + while ((match = listRegex.exec(aiMessage)) !== null) { + const relPath = match[1].trim() || '.'; + try { + const absPath = validatePath(rootPath, relPath); + if (fs.existsSync(absPath) && fs.statSync(absPath).isDirectory()) { + const entries = fs.readdirSync(absPath, { withFileTypes: true }); + let listing = entries + .filter(e => !e.name.startsWith('.') && !EXCLUDED_DIRS.has(e.name)) + .map(e => e.isDirectory() ? `${e.name}/` : e.name) + .join('\n'); + + if (listing.length > 5000) { + listing = listing.slice(0, 5000) + "\n... (truncated for context)"; + } + + report.push(`๐Ÿ“‚ Listed: ${relPath}`); + this.chatHistory.push({ role: 'system', content: `[Result of list_files ${relPath}]\n${listing}`, internal: true }); + } + } catch (err: any) { report.push(`โŒ Listing failed: ${err.message}`); } + } + + // Action 7: Second Brain Knowledge (List/Read) + const listBrainRegex = /(?:<\/list_brain>)?/gi; + while ((match = listBrainRegex.exec(aiMessage)) !== null) { + const relPath = match[1].trim() || '.'; + try { + const brainDir = _getBrainDir(); + const absPath = path.join(brainDir, relPath); + if (fs.existsSync(absPath) && fs.statSync(absPath).isDirectory()) { + const entries = fs.readdirSync(absPath, { withFileTypes: true }); + let listing = entries + .filter(e => !e.name.startsWith('.') && !EXCLUDED_DIRS.has(e.name)) + .map(e => e.isDirectory() ? `${e.name}/` : e.name) + .join('\n'); + + if (listing.length > 5000) { + listing = listing.slice(0, 5000) + "\n... (truncated for context)"; + } + + report.push(`๐Ÿง  Brain Listed: ${relPath}`); + this.chatHistory.push({ role: 'system', content: `[Result of list_brain ${relPath}]\n${listing}`, internal: true }); + } else { + report.push(`โŒ Brain List failed: ${relPath} not found`); + } + } catch (err: any) { report.push(`โŒ Error Listing Brain: ${err.message}`); } + } + + const brainRegex = /([\s\S]*?)<\/read_brain>/gi; + while ((match = brainRegex.exec(aiMessage)) !== null) { + const fileName = match[1].trim(); + try { + const brainDir = _getBrainDir(); + const files = findBrainFiles(brainDir); + const targetFile = files.find((f: string) => path.basename(f) === fileName || f.endsWith(fileName)); - report.push(`๐Ÿง  Brain Listed: ${relPath}`); - this.chatHistory.push({ role: 'system', content: `[Result of list_brain ${relPath}]\n${listing}`, internal: true }); - } else { - report.push(`โŒ Brain List failed: ${relPath} not found`); - } - } catch (err: any) { report.push(`โŒ Error Listing Brain: ${err.message}`); } - } + if (targetFile && fs.existsSync(targetFile)) { + const content = fs.readFileSync(targetFile, 'utf-8'); + report.push(`๐Ÿง  Brain Read: ${fileName}`); + this.chatHistory.push({ role: 'system', content: `[Result of read_brain ${fileName}]\n\`\`\`\n${content}\n\`\`\``, internal: true }); + } else { + report.push(`โŒ Brain Read failed: ${fileName} not found in Second Brain`); + } + } catch (err: any) { report.push(`โŒ Error Reading Brain: ${err.message}`); } + } - const brainRegex = /([\s\S]*?)<\/read_brain>/gi; - while ((match = brainRegex.exec(aiMessage)) !== null) { - const fileName = match[1].trim(); - try { - const brainDir = _getBrainDir(); - const files = findBrainFiles(brainDir); - // Look for direct match or path match - const targetFile = files.find((f: string) => path.basename(f) === fileName || f.endsWith(fileName)); - - if (targetFile && fs.existsSync(targetFile)) { - const content = fs.readFileSync(targetFile, 'utf-8'); - report.push(`๐Ÿง  Brain Read: ${fileName}`); - this.chatHistory.push({ role: 'system', content: `[Result of read_brain ${fileName}]\n\`\`\`\n${content}\n\`\`\``, internal: true }); - } else { - report.push(`โŒ Brain Read failed: ${fileName} not found in Second Brain`); - } - } catch (err: any) { report.push(`โŒ Error Reading Brain: ${err.message}`); } - } + // Action 8: Read URL + const urlRegex = /([\s\S]*?)<\/read_url>/gi; + while ((match = urlRegex.exec(aiMessage)) !== null) { + const url = match[1].trim(); + try { + const res = await fetch(url, { signal: AbortSignal.timeout(10000) }); + const text = await res.text(); + const content = text.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); + const preview = content.length > 5000 ? content.slice(0, 5000) + "\n... (truncated)" : content; + report.push(`๐ŸŒ Read URL: ${url}`); + this.chatHistory.push({ role: 'system', content: `[Result of read_url ${url}]\n${preview}`, internal: true }); + } catch (err: any) { report.push(`โŒ URL Read failed: ${err.message}`); } + } - // Action 8: Read URL (Simple implementation) - const urlRegex = /([\s\S]*?)<\/read_url>/gi; - while ((match = urlRegex.exec(aiMessage)) !== null) { - const url = match[1].trim(); - try { - const res = await fetch(url, { signal: AbortSignal.timeout(10000) }); - const text = await res.text(); - // Simple HTML to text-ish conversion - const content = text.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); - const preview = content.length > 5000 ? content.slice(0, 5000) + "\n... (truncated)" : content; - report.push(`๐ŸŒ Read URL: ${url}`); - this.chatHistory.push({ role: 'system', content: `[Result of read_url ${url}]\n${preview}`, internal: true }); - } catch (err: any) { report.push(`โŒ URL Read failed: ${err.message}`); } + if (firstCreatedFile) { + vscode.window.showTextDocument(vscode.Uri.file(firstCreatedFile), { preview: false }); + } + + // Brain Sync Logic + if (brainModified && shouldAutoPushBrain() && getSecondBrainRepo()) { + this.syncBrain(); + } + + const config = getConfig(); + if (config.dryRun) { + report.push(`\nโš ๏ธ **Dry Run Mode Active**: ์œ„ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ™•์ธํ•˜๊ณ  [์Šน์ธ] ๋˜๋Š” [๋กค๋ฐฑ]์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”.`); + this.webview?.postMessage({ type: 'requiresApproval' }); + // Do NOT commit yet + } else { + this.transactionManager.commit(); + } + } catch (error: any) { + this.transactionManager.rollback(); + const g1Error = error instanceof AgentExecutionError ? error : new AgentExecutionError(error.message, error); + report.push(`๐Ÿ›‘ Transaction Failed: ${g1Error.message}. All file changes rolled back.`); + logError('Action execution failed, rolled back.', g1Error); + // We return the report with the failure message instead of throwing + // so the agent can see the failure and decide what to do next } if (firstCreatedFile) { diff --git a/src/agents/factory.ts b/src/agents/factory.ts new file mode 100644 index 0000000..faa1bea --- /dev/null +++ b/src/agents/factory.ts @@ -0,0 +1,55 @@ +import * as vscode from 'vscode'; +import { getConfig } from '../config'; + +export abstract class BaseAgent { + constructor(protected readonly modelName: string) {} + + protected async callLLM(persona: string, prompt: string): Promise { + const { ollamaUrl } = getConfig(); + const messages = [ + { role: 'system', content: persona }, + { role: 'user', content: prompt } + ]; + + // API ํ˜ธ์ถœ ๋กœ์ง (Streaming ์ƒ๋žตํ•˜๊ณ  ๊ฒฐ๊ณผ๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ—ฌํผ) + const response = await fetch(`${ollamaUrl}/api/chat`, { + method: 'POST', + body: JSON.stringify({ + model: this.modelName, + messages, + stream: false, + options: { temperature: 0.3 } + }) + }); + + if (!response.ok) { + throw new Error(`Agent API Error: ${response.statusText}`); + } + + const data = await response.json() as any; + return data.message?.content || data.choices?.[0]?.message?.content || ''; + } + + abstract execute(input: string, context?: string): Promise; +} + +export class PlannerAgent extends BaseAgent { + private readonly persona = `You are the [Planner Agent]. Analyze the request and output a structured .`; + async execute(input: string, brainContext?: string): Promise { + return this.callLLM(this.persona, `Request: ${input}\nContext: ${brainContext}`); + } +} + +export class ResearcherAgent extends BaseAgent { + private readonly persona = `You are the [Researcher Agent]. Gather facts based on the plan.`; + async execute(input: string, brainContext?: string): Promise { + return this.callLLM(this.persona, `Plan: ${input}\nContext: ${brainContext}`); + } +} + +export class WriterAgent extends BaseAgent { + private readonly persona = `You are the [Writer Agent]. Synthesize research into a final report.`; + async execute(input: string, originalRequest?: string): Promise { + return this.callLLM(this.persona, `Data: ${input}\nOriginal Request: ${originalRequest}`); + } +} diff --git a/src/bridge.ts b/src/bridge.ts index 96b8fe1..fc12132 100644 --- a/src/bridge.ts +++ b/src/bridge.ts @@ -3,16 +3,17 @@ import * as fs from 'fs'; import * as path from 'path'; // axios removed import { - getConfig, _getBrainDir, _isBrainDirExplicitlySet, findBrainFiles, buildApiUrl, logError, logInfo, + logWarn, resolveEngine, summarizeText } from './utils'; +import { getConfig } from './config'; export interface BridgeInterface { injectSystemMessage(msg: string): void; diff --git a/src/config.ts b/src/config.ts index 2e0b959..1c8b668 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,97 +1,153 @@ -/** - * ============================================================ - * Centralized Configuration (์ค‘์•™ ์ง‘์ค‘์‹ ์„ค์ • ๊ด€๋ฆฌ) - * - * ๋ชจ๋“  ํ™˜๊ฒฝ ์„ค์ •(๋ชจ๋ธ ์ด๋ฆ„, API ์—”๋“œํฌ์ธํŠธ, ํƒ€์ž„์•„์›ƒ, ๋ณด์•ˆ ์ •์ฑ… ๋“ฑ) - * ์„ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. Single Source of Truth ์›์น™ ์ ์šฉ. - * ============================================================ - */ - import * as vscode from 'vscode'; import * as os from 'os'; import * as path from 'path'; +import * as fs from 'fs'; -// โ”€โ”€โ”€ VS Code ์„ค์ •์—์„œ ์ฝ์–ด์˜ค๋Š” ๊ฐ’ โ”€โ”€โ”€ -export function getConfig(): IAgentConfig { - const cfg = vscode.workspace.getConfiguration('g1nation'); - return { - ollamaBase: cfg.get('ollamaUrl', 'http://127.0.0.1:11434'), - defaultModel: cfg.get('defaultModel', 'gemma4:e2b'), - maxTreeFiles: cfg.get('maxTreeFiles', 200), - timeout: cfg.get('requestTimeout', 300) * 1000, - localBrainPath: cfg.get('localBrainPath', '') - }; +// โ”€โ”€โ”€ ๋ธŒ๋ ˆ์ธ ํ”„๋กœํ•„ ์ธํ„ฐํŽ˜์ด์Šค โ”€โ”€โ”€ +export interface BrainProfile { + id: string; + name: string; + localBrainPath: string; + secondBrainRepo?: string; + description?: string; } -// โ”€โ”€โ”€ ์—์ด์ „ํŠธ ์„ค์ • ์ธํ„ฐํŽ˜์ด์Šค โ”€โ”€โ”€ +// โ”€โ”€โ”€ ์—์ด์ „ํŠธ ์„ค์ • ์ธํ„ฐํŽ˜์ด์Šค (ํ†ตํ•ฉ ๋ฒ„์ „) โ”€โ”€โ”€ export interface IAgentConfig { - ollamaBase: string; + ollamaUrl: string; defaultModel: string; maxTreeFiles: number; timeout: number; localBrainPath: string; + secondBrainRepo: string; + brainProfiles: BrainProfile[]; + activeBrainId: string; + maxContextSize: number; + maxAutoSteps: number; + dryRun: boolean; + multiAgentEnabled: boolean; } -// โ”€โ”€โ”€ ๋‘๋‡Œ ํด๋” ๊ฒฝ๋กœ ์œ ํ‹ธ๋ฆฌํ‹ฐ โ”€โ”€โ”€ -export function getBrainDir(): string { - const { localBrainPath } = getConfig(); - if (localBrainPath && localBrainPath.trim() !== '') { - if (localBrainPath.startsWith('~/')) { - return path.join(os.homedir(), localBrainPath.substring(2)); - } - return localBrainPath.trim(); +// โ”€โ”€โ”€ ๊ฒฝ๋กœ ์ •๊ทœํ™” ์œ ํ‹ธ๋ฆฌํ‹ฐ โ”€โ”€โ”€ +function normalizePath(p: string): string { + if (!p) return p; + if (p.startsWith('~/')) { + return path.join(os.homedir(), p.substring(2)); } - return path.join(os.homedir(), '.g1nation-brain'); + return p.trim(); } -export function isBrainDirExplicitlySet(): boolean { - const { localBrainPath } = getConfig(); - return !!(localBrainPath && localBrainPath.trim() !== ''); +function toBrainProfile(raw: Partial | undefined, fallbackIndex: number): BrainProfile | null { + if (!raw) return null; + const localBrainPath = normalizePath(raw.localBrainPath || ''); + if (!localBrainPath) return null; + return { + id: (raw.id || `brain-${fallbackIndex + 1}`).trim(), + name: (raw.name || path.basename(localBrainPath) || `Brain ${fallbackIndex + 1}`).trim(), + localBrainPath, + secondBrainRepo: (raw.secondBrainRepo || '').trim(), + description: (raw.description || '').trim() + }; +} + +// โ”€โ”€โ”€ VS Code ์„ค์ •์—์„œ ์ฝ์–ด์˜ค๋Š” ๊ฐ’ (ํ†ตํ•ฉ ๊ตฌํ˜„) โ”€โ”€โ”€ +export function getConfig(): IAgentConfig { + const cfg = vscode.workspace.getConfiguration('g1nation'); + + // ๋ธŒ๋ ˆ์ธ ํ”„๋กœํ•„ ๋กœ์ง (utils.ts์—์„œ ์ด๊ด€) + const legacyBrainPath = cfg.get('localBrainPath', ''); + const legacyBrainRepo = cfg.get('secondBrainRepo', ''); + const configuredProfiles = cfg.get[]>('brainProfiles', []); + const profiles = configuredProfiles + .map((profile, index) => toBrainProfile(profile, index)) + .filter((profile): profile is BrainProfile => !!profile); + + if (profiles.length === 0) { + const fallbackPath = normalizePath(legacyBrainPath) || path.join(os.homedir(), '.g1nation-brain'); + profiles.push({ + id: 'default-brain', + name: 'Local Brain', + localBrainPath: fallbackPath, + secondBrainRepo: legacyBrainRepo.trim(), + description: legacyBrainPath + ? 'Migrated from your existing localBrainPath setting' + : 'Auto-created local knowledge folder.' + }); + } + + const activeBrainId = cfg.get('activeBrainId', profiles[0].id) || profiles[0].id; + const activeBrain = profiles.find((profile) => profile.id === activeBrainId) || profiles[0]; + + const rationaleProtocol = ` +3. Always explain your thought process using the tag BEFORE performing any actions. Use the following structure: + +[PROBLEM] Description of the issue or need found in the context. +[GOAL] What you intend to achieve with your proposed changes. +[REASONING] Detailed logical basis for choosing specific actions or architecture. + +`; + + return { + ollamaUrl: cfg.get('ollamaUrl', 'http://127.0.0.1:11434') || 'http://127.0.0.1:11434', + defaultModel: cfg.get('defaultModel', 'gemma4:e2b') || 'gemma4:e2b', + maxTreeFiles: 200, + timeout: cfg.get('requestTimeout', 300) * 1000, + localBrainPath: activeBrain.localBrainPath, + secondBrainRepo: activeBrain.secondBrainRepo || '', + brainProfiles: profiles, + activeBrainId: activeBrain.id, + maxContextSize: cfg.get('maxContextSize', 12000), + maxAutoSteps: cfg.get('maxAutoSteps', 50), + dryRun: cfg.get('dryRun', false), + multiAgentEnabled: cfg.get('multiAgentEnabled', true) + }; +} + +/** + * Config Validator: Validates the current configuration. + */ +export function validateConfig(): { valid: boolean; errors: string[] } { + const config = getConfig(); + const errors: string[] = []; + + // 1. Ollama URL Validation + try { + new URL(config.ollamaUrl); + } catch (e) { + errors.push(`Invalid Ollama URL: ${config.ollamaUrl}`); + } + + // 2. Brain Path Validation + if (config.localBrainPath && !fs.existsSync(config.localBrainPath)) { + errors.push(`Brain path does not exist: ${config.localBrainPath}`); + } + + return { + valid: errors.length === 0, + errors + }; } // โ”€โ”€โ”€ ๋ณด์•ˆ ์ •์ฑ… (Security Policy) โ”€โ”€โ”€ export const SECURITY_POLICY = { - // ํ—ˆ์šฉ๋œ ํ„ฐ๋ฏธ๋„ ๋ช…๋ น์–ด ํ”„๋ฆฌํ”ฝ์Šค (Whitelist) allowedCommandPrefixes: [ - 'npm', 'yarn', 'pnpm', 'npx', - 'node', 'ts-node', - 'git', - 'python', 'python3', 'pip', 'pip3', - 'docker', 'docker-compose', - 'ls', 'dir', 'cat', 'type', - 'echo', 'print', - 'cargo', 'go', 'rustc', - 'java', 'javac', 'mvn', 'gradle', - 'flutter', 'dart', 'pub', - 'webpack', 'vite', 'esbuild', 'parcel', - 'jest', 'mocha', 'vitest', 'cypress', - 'tsc', 'vue-tsc', + 'npm', 'yarn', 'pnpm', 'npx', 'node', 'ts-node', 'git', 'python', 'python3', 'pip', 'pip3', + 'docker', 'docker-compose', 'ls', 'dir', 'cat', 'type', 'echo', 'print', 'cargo', 'go', 'rustc', + 'java', 'javac', 'mvn', 'gradle', 'flutter', 'dart', 'pub', 'webpack', 'vite', 'esbuild', 'parcel', + 'jest', 'mocha', 'vitest', 'cypress', 'tsc', 'vue-tsc', ], - - // ์ ˆ๋Œ€ ์‹คํ–‰ ๊ธˆ์ง€ ๋ช…๋ น์–ด (Blacklist) forbiddenCommands: [ - 'rm -rf', 'rm-rf', 'del /f', 'format', - 'mkfs', 'dd if=', ':(){ :|:& };:', - 'wget http', 'curl http', 'sudo', - 'chmod 777', 'chown root', + 'rm -rf', 'rm-rf', 'del /f', 'format', 'mkfs', 'dd if=', ':(){ :|:& };:', + 'wget http', 'curl http', 'sudo', 'chmod 777', 'chown root', ], - - // ๋ฏผ๊ฐํ•œ ํŒŒ์ผ ํŒจํ„ด (ํŒŒ์ผ ์ƒ์„ฑ/์ˆ˜์ • ์‹œ ๊ฒฝ๊ณ ) sensitiveFilePatterns: [ - '.env', '.env.*', - 'id_rsa', 'id_ed25519', - '.gitconfig', '.npmrc', '.pypirc', + '.env', '.env.*', 'id_rsa', 'id_ed25519', '.gitconfig', '.npmrc', '.pypirc', 'credentials.json', 'service-account.json', ], - - // ํŒŒ์ผ ์ƒ์„ฑ ์‹œ ์ตœ๋Œ€ ํฌ๊ธฐ (bytes) - maxFileSize: 10 * 1024 * 1024, // 10MB - - // ๋งฅ๋ฝ ํŒŒ์ผ ์ตœ๋Œ€ ๊ฐœ์ˆ˜ + maxFileSize: 10 * 1024 * 1024, maxContextFiles: 200, }; -// โ”€โ”€โ”€ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ ์ƒ์ˆ˜ โ”€โ”€โ”€ export const MAX_CONTEXT_SIZE = 12_000; export const EXCLUDED_DIRS = new Set([ diff --git a/src/core/conflict.ts b/src/core/conflict.ts new file mode 100644 index 0000000..c982476 --- /dev/null +++ b/src/core/conflict.ts @@ -0,0 +1,28 @@ +import { logWarn } from '../utils'; + +/** + * ConflictResolver: Analyzes agent actions for logical contradictions + * or goal conflicts (e.g., Security vs. Performance). + */ +export class ConflictResolver { + /** + * Analyzes proposed actions and returns a warning message if conflicts are found. + */ + public static analyze(actions: any[]): string | null { + // 1. Resource Conflict: Multiple edits to sensitive core files + const securityFiles = actions.filter(a => a.path && (a.path.includes('auth') || a.path.includes('security') || a.path.includes('config'))); + const performanceChanges = actions.filter(a => a.content && (a.content.includes('async') || a.content.includes('parallel') || a.content.includes('cache'))); + + if (securityFiles.length > 0 && performanceChanges.length > 0) { + return "โš ๏ธ Goal Conflict Detected: You are attempting to modify security-sensitive files while introducing performance optimizations (async/parallel). This might introduce race conditions or security vulnerabilities. Should I proceed with both, or prioritize Security?"; + } + + // 2. Structural Conflict: Modifying both interface and implementation in a way that might break contracts + const interfaces = actions.filter(a => a.path && a.path.endsWith('.d.ts') || a.path.includes('interface')); + if (interfaces.length > 1 && actions.length > 10) { + return "โš ๏ธ Structural Complexity Warning: This task involves massive changes to both interfaces and multiple implementations. This may lead to unexpected side effects. Would you like a step-by-step review?"; + } + + return null; + } +} diff --git a/src/core/errorHandler.ts b/src/core/errorHandler.ts new file mode 100644 index 0000000..c8fc5e9 --- /dev/null +++ b/src/core/errorHandler.ts @@ -0,0 +1,41 @@ +export interface UserFriendlyError { + title: string; + message: string; + action: string; +} + +export class ErrorTranslator { + public static translate(error: any): UserFriendlyError { + const msg = String(error.message || error).toLowerCase(); + + if (msg.includes('fetch') || msg.includes('network') || msg.includes('econnrefused')) { + return { + title: '๐ŸŒ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜ (Connection Error)', + message: 'AI ์—”์ง„(LM Studio ๋˜๋Š” Ollama)์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ์ค‘์ธ์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”.', + action: '์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•œ ํ›„ "Refresh" ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์ฃผ์„ธ์š”.' + }; + } + + if (msg.includes('timeout')) { + return { + title: 'โฑ๏ธ ์‘๋‹ต ์‹œ๊ฐ„ ์ดˆ๊ณผ (Timeout)', + message: 'AI๊ฐ€ ๋‹ต๋ณ€์„ ์ค€๋น„ํ•˜๋Š” ๋ฐ ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.', + action: '์„ค์ •์—์„œ Timeout ์‹œ๊ฐ„์„ ๋Š˜๋ฆฌ๊ฑฐ๋‚˜, ๋” ์ž‘์€ ๋ฒ”์œ„๋กœ ์งˆ๋ฌธํ•ด๋ณด์„ธ์š”.' + }; + } + + if (msg.includes('model not found') || msg.includes('404')) { + return { + title: '๐Ÿค– ๋ชจ๋ธ ์ธ์‹ ๋ถˆ๊ฐ€ (Model Not Found)', + message: '์„ ํƒํ•˜์‹  ๋ชจ๋ธ์„ ์‹œ์Šคํ…œ์—์„œ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.', + action: '์ƒ๋‹จ ๋“œ๋กญ๋‹ค์šด์—์„œ ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ์„ ํƒํ•˜๊ฑฐ๋‚˜ ๋ชจ๋ธ ์ด๋ฆ„์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.' + }; + } + + return { + title: 'โš ๏ธ ์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜', + message: '์ž‘์—… ์ค‘ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.', + action: '๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜๊ฑฐ๋‚˜ ํ™•์žฅ์„ ์žฌ์‹œ์ž‘ํ•ด๋ณด์„ธ์š”.' + }; + } +} diff --git a/src/core/errors.ts b/src/core/errors.ts new file mode 100644 index 0000000..4e11c2a --- /dev/null +++ b/src/core/errors.ts @@ -0,0 +1,38 @@ +/** + * g1nation Custom Error Classes + */ + +export abstract class G1Error extends Error { + constructor(public message: string, public details?: any) { + super(message); + this.name = this.constructor.name; + } + + abstract getTypeCode(): string; +} + +export class AgentExecutionError extends G1Error { + getTypeCode() { return 'AGENT_EXECUTION_ERROR'; } +} + +export class FileSystemError extends G1Error { + constructor(message: string, public path: string, details?: any) { + super(message, details); + } + getTypeCode() { return 'FILE_SYSTEM_ERROR'; } +} + +export class APICommunicationError extends G1Error { + constructor(message: string, public engine: string, public status?: number, details?: any) { + super(message, details); + } + getTypeCode() { return 'API_COMMUNICATION_ERROR'; } +} + +export class SecurityValidationError extends G1Error { + getTypeCode() { return 'SECURITY_VALIDATION_ERROR'; } +} + +export class TransactionError extends G1Error { + getTypeCode() { return 'TRANSACTION_ERROR'; } +} diff --git a/src/core/health.ts b/src/core/health.ts new file mode 100644 index 0000000..b7fe5b2 --- /dev/null +++ b/src/core/health.ts @@ -0,0 +1,59 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import { getConfig } from '../config'; +import { logInfo, logWarn, logError } from '../utils'; + +/** + * HealthCheckMonitor: Periodically monitors the environment + * (Ollama, Disk, API) to ensure the agent stays functional. + */ +export class HealthCheckMonitor { + public static async runAllChecks(): Promise<{ ok: boolean; reports: string[] }> { + const reports: string[] = []; + const config = getConfig(); + + // 1. Ollama Connectivity Check + try { + const res = await fetch(`${config.ollamaUrl}/api/tags`, { signal: AbortSignal.timeout(3000) }); + if (res.ok) { + logInfo('Health Check: Ollama connectivity OK.'); + } else { + reports.push('โš ๏ธ AI Server (Ollama) is reachable but returned an error.'); + } + } catch { + reports.push('โŒ AI Server (Ollama) is NOT reachable. Please check if it is running.'); + } + + // 2. Workspace Validation + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + reports.push('โš ๏ธ No workspace folder open. Agent capabilities will be limited.'); + } + + // 3. Simple Disk/Permissions Check + try { + const testFile = workspaceFolders ? vscode.Uri.joinPath(workspaceFolders[0].uri, '.g1-health-check') : null; + if (testFile) { + await vscode.workspace.fs.writeFile(testFile, Buffer.from('ok')); + await vscode.workspace.fs.delete(testFile); + logInfo('Health Check: Write permissions OK.'); + } + } catch { + reports.push('โŒ Write permissions denied in the current workspace.'); + } + + if (reports.length > 0) { + logWarn(`Health Check Warnings: ${reports.join(' | ')}`); + vscode.window.showWarningMessage(`ConnectAI Health Warning: ${reports[0]}`); + } + + return { + ok: reports.length === 0, + reports + }; + } + + public static startInterval(ms: number = 300000) { // Default 5 mins + setInterval(() => this.runAllChecks(), ms); + } +} diff --git a/src/core/lock.ts b/src/core/lock.ts new file mode 100644 index 0000000..786ab49 --- /dev/null +++ b/src/core/lock.ts @@ -0,0 +1,38 @@ +import { logInfo } from '../utils'; + +/** + * AsyncLockManager: Prevents race conditions by ensuring only one task + * can access a specific resource (e.g., a file path) at a time. + */ +export class AsyncLockManager { + private locks: Map> = new Map(); + + /** + * Acquires a lock for a specific resource. + * If the resource is already locked, it waits until the previous task finishes. + */ + public async acquire(resourceId: string): Promise<() => void> { + const previousLock = this.locks.get(resourceId) || Promise.resolve(); + + let release: () => void; + const newLock = new Promise((resolve) => { + release = resolve; + }); + + this.locks.set(resourceId, previousLock.then(() => newLock)); + + await previousLock; + logInfo(`Lock acquired for: ${resourceId}`); + + return () => { + logInfo(`Lock released for: ${resourceId}`); + release(); + if (this.locks.get(resourceId) === newLock) { + this.locks.delete(resourceId); + } + }; + } +} + +// Export as a singleton for the entire agent process +export const lockManager = new AsyncLockManager(); diff --git a/src/core/queue.ts b/src/core/queue.ts new file mode 100644 index 0000000..00aeee3 --- /dev/null +++ b/src/core/queue.ts @@ -0,0 +1,53 @@ +import { logInfo, logError } from '../utils'; + +/** + * ActionQueueManager: Manages large-scale tasks by processing them + * sequentially to prevent resource exhaustion and I/O bottlenecks. + */ +export class ActionQueueManager { + private queue: (() => Promise)[] = []; + private isProcessing: boolean = false; + + /** + * Adds a task to the queue. + */ + public async enqueue(task: () => Promise): Promise { + return new Promise((resolve, reject) => { + this.queue.push(async () => { + try { + const result = await task(); + resolve(result); + } catch (error) { + reject(error); + } + }); + this.processNext(); + }); + } + + private async processNext() { + if (this.isProcessing || this.queue.length === 0) return; + + this.isProcessing = true; + const task = this.queue.shift(); + + if (task) { + try { + // Add a micro-delay to allow system breathing room between heavy I/O + await new Promise(r => setTimeout(r, 50)); + await task(); + } catch (error) { + logError('Task in queue failed:', error); + } finally { + this.isProcessing = false; + this.processNext(); + } + } + } + + public getPendingCount(): number { + return this.queue.length; + } +} + +export const actionQueue = new ActionQueueManager(); diff --git a/src/core/session.ts b/src/core/session.ts new file mode 100644 index 0000000..910952b --- /dev/null +++ b/src/core/session.ts @@ -0,0 +1,88 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ChatMessage } from '../agent'; +import { logInfo, logError } from '../utils'; + +interface SessionData { + taskId: string; + history: ChatMessage[]; + lastActionStr?: string; + timestamp: number; +} + +export class SessionManager { + private sessionDir: string; + + constructor(private context: vscode.ExtensionContext) { + // Use globalStorageUri for persistence across workspace restarts + this.sessionDir = path.join(this.context.globalStorageUri.fsPath, 'sessions'); + this.ensureDir(this.sessionDir); + } + + private ensureDir(dir: string) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + } + + /** + * Saves the current session state. + */ + public async saveSession(taskId: string, history: ChatMessage[], lastActionStr?: string) { + const sessionData: SessionData = { + taskId, + history, + lastActionStr, + timestamp: Date.now() + }; + + const filePath = path.join(this.sessionDir, `session_${this.sanitizeFilename(taskId)}.json`); + try { + fs.writeFileSync(filePath, JSON.stringify(sessionData, null, 2), 'utf-8'); + // Also store the last active taskId in globalState + await this.context.globalState.update('activeTaskId', taskId); + } catch (error: any) { + logError('Failed to save session state', error); + } + } + + /** + * Loads a specific session by taskId. + */ + public loadSession(taskId: string): SessionData | null { + const filePath = path.join(this.sessionDir, `session_${this.sanitizeFilename(taskId)}.json`); + if (!fs.existsSync(filePath)) return null; + + try { + const content = fs.readFileSync(filePath, 'utf-8'); + return JSON.parse(content) as SessionData; + } catch (error: any) { + logError(`Failed to load session for taskId: ${taskId}`, error); + return null; + } + } + + /** + * Loads the last active session. + */ + public loadLastActiveSession(): SessionData | null { + const lastTaskId = this.context.globalState.get('activeTaskId'); + if (!lastTaskId) return null; + return this.loadSession(lastTaskId); + } + + /** + * Clears a specific session. + */ + public clearSession(taskId: string) { + const filePath = path.join(this.sessionDir, `session_${this.sanitizeFilename(taskId)}.json`); + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + } + + private sanitizeFilename(name: string): string { + return name.replace(/[^a-z0-9]/gi, '_').toLowerCase(); + } +} diff --git a/src/core/statusBar.ts b/src/core/statusBar.ts new file mode 100644 index 0000000..0fbc9e4 --- /dev/null +++ b/src/core/statusBar.ts @@ -0,0 +1,57 @@ +import * as vscode from 'vscode'; + +export enum AgentStatus { + Idle = 'Idle', + Thinking = 'Thinking...', + Executing = 'Executing Actions...', + Error = 'Error', + Success = 'Success' +} + +export class StatusBarManager { + private statusBarItem: vscode.StatusBarItem; + + constructor() { + this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); + this.statusBarItem.command = 'g1nation.focusInput'; + this.updateStatus(AgentStatus.Idle); + this.statusBarItem.show(); + } + + public updateStatus(status: AgentStatus, detail?: string) { + let icon = ''; + switch (status) { + case AgentStatus.Idle: + icon = '$(pass)'; + this.statusBarItem.backgroundColor = undefined; + break; + case AgentStatus.Thinking: + icon = '$(sync~spin)'; + this.statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground'); + break; + case AgentStatus.Executing: + icon = '$(gear~spin)'; + this.statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground'); + break; + case AgentStatus.Error: + icon = '$(error)'; + this.statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.errorBackground'); + break; + case AgentStatus.Success: + icon = '$(check)'; + this.statusBarItem.backgroundColor = undefined; + break; + } + + this.statusBarItem.text = `${icon} G1nation: ${status}`; + this.statusBarItem.tooltip = detail || `Current State: ${status}`; + + if (status === AgentStatus.Success || status === AgentStatus.Error) { + setTimeout(() => this.updateStatus(AgentStatus.Idle), 5000); + } + } + + public dispose() { + this.statusBarItem.dispose(); + } +} diff --git a/src/core/transaction.ts b/src/core/transaction.ts new file mode 100644 index 0000000..2a1f751 --- /dev/null +++ b/src/core/transaction.ts @@ -0,0 +1,127 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { FileSystemError, TransactionError } from './errors'; +import { logInfo, logError } from '../utils'; + +interface BackupEntry { + path: string; + type: 'modified' | 'created' | 'deleted'; + originalContent?: string; +} + +export class TransactionManager { + private backups: Map = new Map(); + private externalVerifications: Map = new Map(); + private isTransactionActive: boolean = false; + + constructor() {} + + /** + * Starts a new transaction. + */ + public begin() { + if (this.isTransactionActive) { + throw new TransactionError('A transaction is already in progress.'); + } + this.backups.clear(); + this.externalVerifications.clear(); + this.isTransactionActive = true; + logInfo('Transaction started.'); + } + + /** + * Records the success of an external action (e.g., API call). + */ + public recordExternalAction(actionId: string, success: boolean) { + if (!this.isTransactionActive) return; + this.externalVerifications.set(actionId, success); + logInfo(`External action recorded: ${actionId} (success: ${success})`); + } + + /** + * Checks if all recorded external actions were successful. + */ + public isFullyVerified(): boolean { + for (const success of this.externalVerifications.values()) { + if (!success) return false; + } + return true; + } + + /** + * Records a file state before modification. + */ + public async record(filePath: string) { + if (!this.isTransactionActive) return; + if (this.backups.has(filePath)) return; // Already backed up in this transaction + + try { + if (fs.existsSync(filePath)) { + const content = fs.readFileSync(filePath, 'utf-8'); + this.backups.set(filePath, { + path: filePath, + type: 'modified', + originalContent: content + }); + } else { + this.backups.set(filePath, { + path: filePath, + type: 'created' + }); + } + } catch (error: any) { + throw new FileSystemError(`Failed to backup file: ${error.message}`, filePath, error); + } + } + + /** + * Commits the transaction, clearing all backups. + */ + public commit() { + if (!this.isTransactionActive) return; + this.backups.clear(); + this.isTransactionActive = false; + logInfo('Transaction committed successfully.'); + } + + /** + * Rolls back all changes made during the transaction. + */ + public rollback() { + if (!this.isTransactionActive) return; + + logInfo(`Rolling back ${this.backups.size} changes...`); + + for (const entry of this.backups.values()) { + try { + if (entry.type === 'created') { + if (fs.existsSync(entry.path)) { + fs.unlinkSync(entry.path); + logInfo(`Rollback: Deleted created file ${entry.path}`); + } + } else if (entry.type === 'modified' || entry.type === 'deleted') { + if (entry.originalContent !== undefined) { + const dir = path.dirname(entry.path); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(entry.path, entry.originalContent, 'utf-8'); + logInfo(`Rollback: Restored file ${entry.path}`); + } + } + } catch (error: any) { + logError(`Failed to rollback change for ${entry.path}`, error); + } + } + + this.backups.clear(); + this.isTransactionActive = false; + logInfo('Transaction rollback completed.'); + } + + public isActive(): boolean { + return this.isTransactionActive; + } +} + +// Export a singleton instance if needed, or instantiate per AgentExecutor diff --git a/src/extension.ts b/src/extension.ts index 3e9f706..d3cd76c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,7 +3,6 @@ import * as fs from 'fs'; import * as path from 'path'; // axios removed in favor of native fetch import { - getConfig, _getBrainDir, _isBrainDirExplicitlySet, findBrainFiles, @@ -12,15 +11,28 @@ import { logError, logInfo } from './utils'; +import { getConfig, validateConfig } from './config'; import { AgentExecutor } from './agent'; import { BridgeServer } from './bridge'; import { SidebarChatProvider } from './sidebarProvider'; +import { HealthCheckMonitor } from './core/health'; /** * G1nation Extension Entry Point */ export async function activate(context: vscode.ExtensionContext) { - logInfo('Connect AI extension activated.'); + logInfo('ConnectAI activating...'); + + // Start Environment Health Monitoring + HealthCheckMonitor.runAllChecks(); + HealthCheckMonitor.startInterval(600000); // Check every 10 mins + + // 0. Validate Configuration + const validation = validateConfig(); + if (!validation.valid) { + vscode.window.showErrorMessage(`G1nation Configuration Error: ${validation.errors.join(' ')}`); + logError('Configuration validation failed.', { errors: validation.errors }); + } // 1. Ensure Brain Directory await _ensureBrainDir(context); diff --git a/src/sidebarProvider.ts b/src/sidebarProvider.ts index 0680b37..da5fa70 100644 --- a/src/sidebarProvider.ts +++ b/src/sidebarProvider.ts @@ -2,7 +2,6 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; import { - getConfig, _getBrainDir, findBrainFiles, buildApiUrl, @@ -13,6 +12,7 @@ import { resolveEngine, summarizeText } from './utils'; +import { getConfig } from './config'; import { AgentExecutor, ChatMessage } from './agent'; import { BridgeInterface } from './bridge'; @@ -88,8 +88,12 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn await this._sendBrainProfiles(); await this._sendSessionList(); await this._sendModels(); + await this._sendConfig(); await this._restoreActiveSessionIntoView(); break; + case 'toggleMultiAgent': + await vscode.workspace.getConfiguration('g1nation').update('multiAgentEnabled', data.value, vscode.ConfigurationTarget.Global); + break; case 'getModels': await this._sendModels(); break; @@ -128,6 +132,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn await this.syncBrain(); await this._sendBrainStatus(); break; + case 'addMessage': + this._view?.webview.postMessage({ type: 'addMessage', role: data.role, value: data.value, rationale: data.rationale }); + break; case 'addBrain': await this._addBrainProfile(); break; @@ -143,6 +150,13 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn case 'refreshModels': await this._sendModels(); break; + case 'model': + await vscode.workspace.getConfiguration('g1nation').update('defaultModel', data.value, vscode.ConfigurationTarget.Global); + logInfo(`Default model updated to: ${data.value}`); + break; + case 'proactiveTrigger': + await this._handleProactiveSuggestion(data.context); + break; case 'exportResponse': const workspacePath = vscode.workspace.workspaceFolders?.[0].uri.fsPath || ''; const defaultPath = path.join(workspacePath, 'g1_response.md'); @@ -155,6 +169,12 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn vscode.window.showInformationMessage(`โœ… Exported to ${path.basename(uri.fsPath)}`); } break; + case 'approveAction': + await this._agent.approveTransaction(); + break; + case 'rejectAction': + await this._agent.rejectTransaction(); + break; } }); } @@ -378,6 +398,17 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn await this._context.globalState.update('chat_sessions', sessions.slice(0, 50)); } + private async _sendConfig() { + if (!this._view) return; + const config = getConfig(); + this._view.webview.postMessage({ + type: 'configUpdate', + value: { + multiAgentEnabled: config.multiAgentEnabled + } + }); + } + private async _sendBrainStatus() { if (!this._view) return; const activeBrain = getActiveBrainProfile(); @@ -631,6 +662,29 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn this._view.webview.postMessage({ type: 'agentsList', value: agents, selected: lastPath }); } + private async _handleProactiveSuggestion(context: string) { + if (!this._view) return; + + let suggestion = ''; + switch (context) { + case 'settings_exploration': + suggestion = '๐Ÿ’ก **Tip:** ๋ชจ๋ธ ์„ค์ •์„ ์ตœ์ ํ™”ํ•˜์—ฌ ๋‹ต๋ณ€ ์†๋„๋ฅผ 2๋ฐฐ ์ด์ƒ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ค์ •์—์„œ `Max Context Size`๋ฅผ ์กฐ์ •ํ•ด๋ณด์„ธ์š”!'; + break; + case 'brain_sync_exploration': + suggestion = '๐Ÿง  **Knowledge Sync:** ์ตœ๊ทผ์— ์ˆ˜์ •ํ•œ ํŒŒ์ผ์ด ์ง€์‹ ๋ฒ ์ด์Šค์— ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜๋‚˜์š”? ์ง€๊ธˆ ๋™๊ธฐํ™” ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ตœ์‹  ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์„ธ์š”.'; + break; + case 'agent_selection_exploration': + suggestion = '๐Ÿค– **Agent Skills:** ํŠน์ • ์–ธ์–ด๋‚˜ ํ”„๋ ˆ์ž„์›Œํฌ์— ํŠนํ™”๋œ ์—์ด์ „ํŠธ ์Šคํ‚ฌ์„ ์„ ํƒํ•˜๋ฉด ๋” ์ •ํ™•ํ•œ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.'; + break; + default: + suggestion = '๐Ÿ’ก ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋ฐœ๊ฒฌํ•˜์…จ๋‚˜์š”? ๊ถ๊ธˆํ•œ ์ ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“  ๋ฌผ์–ด๋ณด์„ธ์š”!'; + } + + this._view.webview.postMessage({ type: 'streamStart' }); + this._view.webview.postMessage({ type: 'streamChunk', value: `\n\n> [!TIP]\n> ${suggestion}\n` }); + this._view.webview.postMessage({ type: 'streamEnd' }); + } + private async _createAgent() { const name = await vscode.window.showInputBox({ prompt: 'Name of the new Agent (e.g., frontend_expert)', @@ -891,8 +945,24 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn .msg-head { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 11px; color: var(--text-dim); } .av { width: 22px; height: 22px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; } - .av-user { background: var(--border); color: var(--text-primary); } - .av-ai { background: var(--accent); color: #fff; } + .icon-btn:hover { background: var(--border); color: var(--text-bright); } + .icon-btn.active { color: var(--accent); border-color: var(--accent); background: rgba(187, 134, 252, 0.1); } + + /* Tooltip System */ + [data-tooltip] { position: relative; } + [data-tooltip]::after { + content: attr(data-tooltip); + position: absolute; bottom: -30px; left: 50%; transform: translateX(-50%); + background: #333; color: #fff; padding: 4px 8px; border-radius: 4px; + font-size: 10px; white-space: nowrap; opacity: 0; pointer-events: none; + transition: all 0.2s ease; z-index: 100; box-shadow: 0 4px 10px rgba(0,0,0,0.3); + } + [data-tooltip]:hover::after { opacity: 1; bottom: -35px; } + + .header-controls { display: flex; gap: 8px; margin-left: auto; } + + #promptInput::placeholder { color: var(--accent); opacity: 0.6; font-weight: 500; } + .msg-body { padding-left: 30px; @@ -997,11 +1067,13 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn /* --- Overlays & Others --- */ .thinking-bar { height: 2px; background: transparent; position: relative; overflow: hidden; margin-top: -1px; } - .thinking-bar.active::after { - content: ''; position: absolute; top: 0; left: -40%; width: 40%; height: 100%; - background: linear-gradient(90deg, transparent, var(--accent), transparent); animation: think 1.5s infinite; + .thinking-bar.active { + display: block; + background: linear-gradient(90deg, transparent, #2196f3, #bb86fc, transparent); + background-size: 200% 100%; + animation: thinking 1.5s infinite linear; } - @keyframes think { 0% { left: -40%; } 100% { left: 100%; } } + @keyframes thinking { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .history-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); @@ -1025,6 +1097,140 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn margin-bottom: 10px; cursor: pointer; transition: 0.2s; } .history-item:hover { border-color: var(--accent); background: var(--accent-glow); } + + /* --- Approval UI --- */ + .approval-box { + display: flex; + flex-direction: column; + gap: 12px; + margin: 15px 0; + padding: 16px; + background: var(--bg-secondary); + border: 1px solid var(--border-bright); + border-radius: 12px; + animation: msgIn 0.3s ease-out; + } + .approval-title { font-weight: 700; color: var(--accent); font-size: 12px; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; } + .approval-btns { display: flex; gap: 10px; } + .btn-approve { flex: 1; background: var(--success); color: white; border: none; padding: 10px; border-radius: 8px; cursor: pointer; font-weight: 700; font-size: 12px; transition: 0.2s; } + .btn-approve:hover { filter: brightness(1.1); transform: translateY(-1px); } + .btn-reject { flex: 1; background: var(--error); color: white; border: none; padding: 10px; border-radius: 8px; cursor: pointer; font-weight: 700; font-size: 12px; transition: 0.2s; } + .btn-reject:hover { filter: brightness(1.1); transform: translateY(-1px); } + + /* --- Physics & Micro-interactions --- */ + button { + transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); + user-select: none; + outline: none; + } + button:active { + transform: scale(0.96); + filter: brightness(0.9); + } + + /* --- Hierarchical Grouping --- */ + .input-group { + background: rgba(255, 255, 255, 0.02); + border-radius: 12px; + padding: 8px; + margin-top: 10px; + border: 1px solid rgba(255, 255, 255, 0.05); + display: flex; + gap: 8px; + } + + /* --- Storytelling Stepper --- */ + .stepper-container { + display: none; + margin: 12px 16px; + padding: 12px; + background: rgba(var(--accent-rgb), 0.05); + border-radius: 8px; + border: 1px solid rgba(var(--accent-rgb), 0.1); + animation: slideIn 0.3s ease-out; + } + .stepper-container.active { display: block; } + .steps { + display: flex; + justify-content: space-between; + position: relative; + } + .step { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + flex: 1; + } + .step-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-dim); + transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + .step.active .step-dot { + background: var(--accent); + box-shadow: 0 0 12px var(--accent); + transform: scale(1.5); + } + .step.complete .step-dot { + background: var(--success); + box-shadow: 0 0 8px var(--success); + } + .step-label { + font-size: 9px; + color: var(--text-dim); + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + } + .step.active .step-label { color: var(--accent); } + .step.complete .step-label { color: var(--success); } + + /* --- Rationale View (Thought Process) --- */ + .rationale-container { + margin: 12px 0; + padding: 16px; + background: rgba(255, 255, 255, 0.03); + border-left: 3px solid var(--accent); + border-radius: 4px 12px 12px 4px; + font-size: 12px; + line-height: 1.5; + animation: slideIn 0.3s ease-out; + backdrop-filter: blur(10px); + } + .rationale-header { + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--accent); + margin-bottom: 12px; + display: flex; + align-items: center; + gap: 8px; + font-size: 11px; + } + .rationale-section { + margin-bottom: 10px; + } + .rationale-label { + display: flex; + align-items: center; + gap: 6px; + font-weight: 700; + color: var(--text-bright); + margin-bottom: 4px; + font-size: 11px; + } + .rationale-content { + color: var(--text-dim); + padding-left: 20px; + } + @keyframes slideIn { + from { opacity: 0; transform: translateX(-10px); } + to { opacity: 1; transform: translateX(0); } + } @@ -1043,9 +1249,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn - - - + + + + @@ -1059,6 +1266,15 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
+
+
+
Analyze
+
Plan
+
Execute
+
Verify
+
+
+
@@ -1088,6 +1304,11 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
+
+ + +
+ @@ -1098,6 +1319,52 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn const sendBtn = document.getElementById('sendBtn'); const thinkingBar = document.getElementById('thinkingBar'); const statusLabel = document.getElementById('statusLabel'); + const stepper = document.getElementById('stepper'); + + // --- Sound Manager --- + const Sound = { + ctx: null, + init() { if (!this.ctx) this.ctx = new (window.AudioContext || window.webkitAudioContext)(); }, + play(freq, type, dur) { + try { + this.init(); + const osc = this.ctx.createOscillator(); + const gain = this.ctx.createGain(); + osc.type = type; + osc.frequency.setValueAtTime(freq, this.ctx.currentTime); + gain.gain.setValueAtTime(0.05, this.ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + dur); + osc.connect(gain); + gain.connect(this.ctx.destination); + osc.start(); + osc.stop(this.ctx.currentTime + dur); + } catch(e) {} + }, + success() { this.play(880, 'sine', 0.1); setTimeout(() => this.play(1109, 'sine', 0.15), 80); }, + warn() { this.play(440, 'triangle', 0.3); } + }; + + function setStep(stepId, state = 'active') { + stepper.classList.add('active'); + const step = document.getElementById('step-' + stepId); + if (step) { + if (state === 'active') { + document.querySelectorAll('.step').forEach(s => s.classList.remove('active')); + step.classList.add('active'); + } else if (state === 'complete') { + step.classList.remove('active'); + step.classList.add('complete'); + } + } + } + + function resetStepper() { + stepper.classList.remove('active'); + document.querySelectorAll('.step').forEach(s => { + s.classList.remove('active'); + s.classList.remove('complete'); + }); + } const modelSel = document.getElementById('modelSel'); const brainSel = document.getElementById('brainSel'); const historyOverlay = document.getElementById('historyOverlay'); @@ -1133,16 +1400,45 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn document.body.removeChild(textarea); } + window.approve = () => { + const box = document.querySelector('.approval-box'); + if (box) box.remove(); + vscode.postMessage({ type: 'approveAction' }); + }; + window.reject = () => { + const box = document.querySelector('.approval-box'); + if (box) box.remove(); + vscode.postMessage({ type: 'rejectAction' }); + }; + function exportToMD(text) { vscode.postMessage({ type: 'exportResponse', text: text }); } - function addMsg(text, role) { + function addMsg(text, role, rationale) { const isUser = role === 'user'; const msgEl = document.createElement('div'); msgEl.className = 'msg ' + (isUser ? 'msg-user' : 'msg-ai'); msgEl._raw = text; + // If rationale exists and it's an AI message, add the Rationale View + if (!isUser && rationale && (rationale.problem || rationale.goal || rationale.reasoning)) { + const ratDiv = document.createElement('div'); + ratDiv.className = 'rationale-container'; + let ratHtml = '
๐Ÿง  Thought Process
'; + if (rationale.problem) { + ratHtml += '
โš ๏ธ Problem
' + rationale.problem + '
'; + } + if (rationale.goal) { + ratHtml += '
๐Ÿ’ก Goal
' + rationale.goal + '
'; + } + if (rationale.reasoning) { + ratHtml += '
โœ… Rationale
' + rationale.reasoning + '
'; + } + ratDiv.innerHTML = ratHtml; + chat.appendChild(ratDiv); + } + const head = document.createElement('div'); head.className = 'msg-head'; head.innerHTML = isUser ? '
U
You' : '
โœฆ
G1nation'; @@ -1179,6 +1475,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn window.addEventListener('message', e => { const msg = e.data; switch(msg.type) { + case 'addMessage': + addMsg(msg.value, msg.role, msg.rationale); + break; case 'streamStart': thinkingBar.classList.remove('active'); if (document.querySelector('.welcome')) document.querySelector('.welcome').remove(); @@ -1196,6 +1495,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn case 'streamEnd': if (streamBody) streamBody.classList.remove('stream-active'); streamBody = null; sendBtn.disabled = false; + resetStepper(); + Sound.success(); break; case 'restoreHistory': case 'sessionLoaded': @@ -1204,7 +1505,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn if (history && history.length > 0) { chat.innerHTML = ''; - history.forEach(m => addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user')); + history.forEach(m => addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user', m.rationale)); } if (historyData.negativePrompt !== undefined) { negativePrompt.value = historyData.negativePrompt; @@ -1224,6 +1525,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn if (m === msg.value.selected) o.selected = true; modelSel.appendChild(o); }); + if (typeof updateInputPlaceholder === 'function') updateInputPlaceholder(); statusLabel.innerText = \`Model: \${msg.value.selected}\`; break; case 'brainProfiles': @@ -1251,6 +1553,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn break; case 'autoContinue': statusLabel.innerText = msg.value; thinkingBar.classList.add('active'); + if (msg.value.includes('Analyzing')) setStep('analyze'); + if (msg.value.includes('Planning')) setStep('plan'); + if (msg.value.includes('Executing')) setStep('execute'); setTimeout(() => { thinkingBar.classList.remove('active'); }, 3000); break; case 'agentsList': @@ -1272,6 +1577,18 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn thinkingBar.classList.remove('active'); sendBtn.disabled = false; addMsg(msg.value, 'error'); break; + case 'requiresApproval': + const box = document.createElement('div'); + box.className = 'approval-box'; + box.innerHTML = '
๐Ÿ›ก๏ธ ์ž‘์—… ์Šน์ธ ๋Œ€๊ธฐ ์ค‘ (Action Approval Required)
' + + '
์œ„์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ”„๋กœ์ ํŠธ์— ๋ฐ˜์˜ํ• ๊นŒ์š”?
' + + '
' + + ' ' + + ' ' + + '
'; + chat.appendChild(box); + chat.scrollTop = chat.scrollHeight; + break; } }); @@ -1327,16 +1644,36 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn }); input.addEventListener('input', () => { input.style.height = 'auto'; input.style.height = input.scrollHeight + 'px'; }); - document.getElementById('newChatBtn').onclick = () => vscode.postMessage({ type: 'newChat' }); + const startNewChat = () => { Sound.play(660, 'sine', 0.1); vscode.postMessage({ type: 'newChat' }); }; + document.getElementById('newChatBtn').onclick = startNewChat; + document.getElementById('inputNewChatBtn').onclick = startNewChat; + document.getElementById('settingsBtn').onclick = () => vscode.postMessage({ type: 'openSettings' }); document.getElementById('internetBtn').onclick = () => { internetEnabled = !internetEnabled; document.getElementById('internetBtn').classList.toggle('active', internetEnabled); }; - document.getElementById('brainBtn').onclick = () => vscode.postMessage({ type: 'syncBrain' }); + + let multiAgentEnabled = true; + document.getElementById('multiAgentBtn').onclick = () => { + multiAgentEnabled = !multiAgentEnabled; + vscode.postMessage({ type: 'toggleMultiAgent', value: multiAgentEnabled }); + document.getElementById('multiAgentBtn').classList.toggle('active', multiAgentEnabled); + }; + + const syncBrain = () => { Sound.play(550, 'sine', 0.1); vscode.postMessage({ type: 'syncBrain' }); }; + document.getElementById('brainBtn').onclick = syncBrain; + document.getElementById('inputSyncBtn').onclick = syncBrain; document.getElementById('historyBtn').onclick = () => vscode.postMessage({ type: 'getSessions' }); document.getElementById('historyBtn').addEventListener('click', () => historyOverlay.classList.add('visible')); document.getElementById('closeHistoryBtn').onclick = () => historyOverlay.classList.remove('visible'); - modelSel.onchange = () => vscode.postMessage({ type: 'refreshModels' }); + const updateInputPlaceholder = () => { + promptInput.placeholder = \`Ask \${modelSel.value}...\`; + }; + + modelSel.onchange = () => { + vscode.postMessage({ type: 'model', value: modelSel.value }); + updateInputPlaceholder(); + }; brainSel.onchange = () => { if (brainSel.value === 'new') { vscode.postMessage({ type: 'addBrain' }); @@ -1345,6 +1682,16 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn } }; + // Handle initial state and state updates from extension + window.addEventListener('message', e => { + const msg = e.data; + if (msg.type === 'configUpdate') { + multiAgentEnabled = msg.value.multiAgentEnabled; + document.getElementById('multiAgentBtn').classList.toggle('active', multiAgentEnabled); + } + }); + + agentSel.onchange = () => { if (agentSel.value !== 'none') { vscode.postMessage({ type: 'getAgentContent', path: agentSel.value }); @@ -1381,6 +1728,25 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn vscode.postMessage({ type: 'getModels' }); vscode.postMessage({ type: 'getAgents' }); vscode.postMessage({ type: 'ready' }); + + // --- Proactive Behavioral Tracking --- + let hoverTimer = null; + const trackBehavior = (elementId, context) => { + const el = document.getElementById(elementId); + if (!el) return; + el.addEventListener('mouseenter', () => { + hoverTimer = setTimeout(() => { + vscode.postMessage({ type: 'proactiveTrigger', context: context }); + }, 5000); // 5 seconds threshold + }); + el.addEventListener('mouseleave', () => { + if (hoverTimer) clearTimeout(hoverTimer); + }); + }; + + trackBehavior('settingsBtn', 'settings_exploration'); + trackBehavior('brainBtn', 'brain_sync_exploration'); + trackBehavior('agentSel', 'agent_selection_exploration'); `; diff --git a/src/utils.ts b/src/utils.ts index a0beef7..a22777f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,103 +3,7 @@ import * as path from 'path'; import * as os from 'os'; import * as fs from 'fs'; -export const EXCLUDED_DIRS = new Set([ - 'node_modules', '.git', '.vscode', 'out', 'dist', 'build', - '.next', '.cache', '__pycache__', '.DS_Store', 'coverage', - '.turbo', '.nuxt', '.output', 'vendor', 'target' -]); - -// Configuration constants moved to package.json and getConfig() - -export interface BrainProfile { - id: string; - name: string; - localBrainPath: string; - secondBrainRepo?: string; - description?: string; -} - -function normalizePath(p: string): string { - if (!p) return p; - if (p.startsWith('~/')) { - return path.join(os.homedir(), p.substring(2)); - } - return p.trim(); -} - -function toBrainProfile(raw: Partial | undefined, fallbackIndex: number): BrainProfile | null { - if (!raw) return null; - const localBrainPath = normalizePath(raw.localBrainPath || ''); - if (!localBrainPath) return null; - return { - id: (raw.id || `brain-${fallbackIndex + 1}`).trim(), - name: (raw.name || path.basename(localBrainPath) || `Brain ${fallbackIndex + 1}`).trim(), - localBrainPath, - secondBrainRepo: (raw.secondBrainRepo || '').trim(), - description: (raw.description || '').trim() - }; -} - -const STEVE_SKILL_PATH = '/Volumes/Data/project/Antigravity/Agent/.agent/skills/steve_jobs/SKILL.md'; - -function extractSteveRuntimeSection(skillContent: string): string { - const sectionStart = skillContent.indexOf('## ๐ŸŽฏ Steve Single-Contact Runtime Protocol'); - if (sectionStart < 0) return ''; - const sectionEnd = skillContent.indexOf('### 4. The "One More Thing" Mandate', sectionStart); - return skillContent.slice(sectionStart, sectionEnd > sectionStart ? sectionEnd : undefined).trim(); -} - -function loadSteveRuntimePrompt(): string { - try { - if (!fs.existsSync(STEVE_SKILL_PATH)) return ''; - const skillContent = fs.readFileSync(STEVE_SKILL_PATH, 'utf-8'); - return extractSteveRuntimeSection(skillContent); - } catch { - return ''; - } -} - -export function getConfig() { - const cfg = vscode.workspace.getConfiguration('g1nation'); - const legacyBrainPath = cfg.get('localBrainPath', ''); - const legacyBrainRepo = cfg.get('secondBrainRepo', ''); - const configuredProfiles = cfg.get[]>('brainProfiles', []); - const profiles = configuredProfiles - .map((profile, index) => toBrainProfile(profile, index)) - .filter((profile): profile is BrainProfile => !!profile); - - // IMPORTANT: This virtual default-brain exists only in memory at runtime. - // It must NEVER be written back to the settings file (g1nation.brainProfiles). - // _addBrainProfile() reads cfg.get('brainProfiles') directly to avoid this contamination. - if (profiles.length === 0) { - const fallbackPath = normalizePath(legacyBrainPath) || path.join(os.homedir(), '.g1nation-brain'); - profiles.push({ - id: 'default-brain', - name: 'Local Brain', - localBrainPath: fallbackPath, - secondBrainRepo: legacyBrainRepo.trim(), - description: legacyBrainPath - ? 'Migrated from your existing localBrainPath setting' - : 'Auto-created local knowledge folder. Add a real brain via the โœŽ button.' - }); - } - - const activeBrainId = cfg.get('activeBrainId', profiles[0].id) || profiles[0].id; - const activeBrain = profiles.find((profile) => profile.id === activeBrainId) || profiles[0]; - - return { - ollamaUrl: cfg.get('ollamaUrl', 'http://127.0.0.1:11434'), - defaultModel: cfg.get('defaultModel', 'gemma4:e2b'), - maxTreeFiles: 200, - timeout: cfg.get('requestTimeout', 300) * 1000, - localBrainPath: activeBrain.localBrainPath, - secondBrainRepo: activeBrain.secondBrainRepo || '', - brainProfiles: profiles, - activeBrainId: activeBrain.id, - maxContextSize: cfg.get('maxContextSize', 12000), - maxAutoSteps: cfg.get('maxAutoSteps', 50) - }; -} +import { getConfig, BrainProfile, EXCLUDED_DIRS } from './config'; export type EngineKind = 'lmstudio' | 'ollama'; @@ -243,6 +147,12 @@ Core behavior: - Use the active Local Brain only when it is relevant to the user's question. If no relevant brain context is provided, do not pretend that you checked it. - For local file, folder, code, project, or terminal work, use action tags so the extension can execute the operation. - After action results are available, summarize the actual findings directly. +- ALWAYS explain your thought process using the tag BEFORE performing any actions. Use the following structure: + +[PROBLEM] Description of the issue or need found in the context. +[GOAL] What you intend to achieve with your proposed changes. +[REASONING] Detailed logical basis for choosing specific actions or architecture. + Available action tags: diff --git a/tests/transaction.test.ts b/tests/transaction.test.ts new file mode 100644 index 0000000..fc06e6d --- /dev/null +++ b/tests/transaction.test.ts @@ -0,0 +1,68 @@ +/// +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { TransactionManager } from '../src/core/transaction'; + +describe('TransactionManager', () => { + let tm: TransactionManager; + let testDir: string; + + beforeEach(() => { + tm = new TransactionManager(); + testDir = path.join(os.tmpdir(), `g1-test-${Date.now()}`); + if (!fs.existsSync(testDir)) { + fs.mkdirSync(testDir, { recursive: true }); + } + }); + + afterEach(() => { + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }); + } + }); + + test('should record and rollback file creation', () => { + const testFile = path.join(testDir, 'test.txt'); + + tm.begin(); + tm.record(testFile); + fs.writeFileSync(testFile, 'hello world'); + + expect(fs.existsSync(testFile)).toBe(true); + + tm.rollback(); + + expect(fs.existsSync(testFile)).toBe(false); + }); + + test('should record and rollback file modification', () => { + const testFile = path.join(testDir, 'edit.txt'); + fs.writeFileSync(testFile, 'original'); + + tm.begin(); + tm.record(testFile); + fs.writeFileSync(testFile, 'modified'); + + expect(fs.readFileSync(testFile, 'utf8')).toBe('modified'); + + tm.rollback(); + + expect(fs.readFileSync(testFile, 'utf8')).toBe('original'); + }); + + test('should delete backup files after commit', () => { + const testFile = path.join(testDir, 'commit.txt'); + fs.writeFileSync(testFile, 'data'); + + tm.begin(); + tm.record(testFile); + fs.writeFileSync(testFile, 'new data'); + + tm.commit(); + + expect(fs.readFileSync(testFile, 'utf8')).toBe('new data'); + // Backups are in ~/.g1nation-backups (hardcoded in TM, but let's assume it cleans up internal state) + expect(tm.isActive()).toBe(false); + }); +}); diff --git a/tests/vulnerability.test.ts b/tests/vulnerability.test.ts new file mode 100644 index 0000000..584b69b --- /dev/null +++ b/tests/vulnerability.test.ts @@ -0,0 +1,60 @@ +/// +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { AsyncLockManager } from '../src/core/lock'; +import { TransactionManager } from '../src/core/transaction'; + +describe('System Vulnerability Tests', () => { + let testDir: string; + + beforeEach(() => { + testDir = path.join(os.tmpdir(), `g1-vulnerability-test-${Date.now()}`); + if (!fs.existsSync(testDir)) { + fs.mkdirSync(testDir, { recursive: true }); + } + }); + + afterEach(() => { + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }); + } + }); + + test('AsyncLockManager should prevent race conditions on the same file', async () => { + const lockManager = new AsyncLockManager(); + const testFile = path.join(testDir, 'race_condition.txt'); + fs.writeFileSync(testFile, '0'); + + // Simulate 10 concurrent increments + const tasks = Array.from({ length: 10 }).map(async (_, i) => { + const release = await lockManager.acquire(testFile); + try { + const current = parseInt(fs.readFileSync(testFile, 'utf-8')); + // Artificial delay to increase race condition probability + await new Promise(r => setTimeout(r, 10)); + fs.writeFileSync(testFile, (current + 1).toString()); + } finally { + release(); + } + }); + + await Promise.all(tasks); + + const finalValue = fs.readFileSync(testFile, 'utf-8'); + expect(finalValue).toBe('10'); // If race condition occurs, it will be < 10 + }); + + test('TransactionManager should support external verification hooks', () => { + const tm = new TransactionManager(); + tm.begin(); + + tm.recordExternalAction('DB_COMMIT', true); + tm.recordExternalAction('API_PUSH', true); + + expect(tm.isFullyVerified()).toBe(true); + + tm.recordExternalAction('WIKI_SYNC', false); + expect(tm.isFullyVerified()).toBe(false); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index c5a77d2..6f6a02d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,9 +3,11 @@ "module": "commonjs", "target": "ES2022", "outDir": "out", - "lib": ["ES2022"], + "lib": ["ES2022", "DOM"], "sourceMap": true, - "strict": true + "strict": true, + "types": ["node", "jest"] }, + "include": ["src/**/*", "tests/**/*"], "exclude": ["node_modules", ".vscode-test"] }