diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..db36fe3
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,25 @@
+{
+ "root": true,
+ "parser": "@typescript-eslint/parser",
+ "parserOptions": {
+ "ecmaVersion": 2022,
+ "sourceType": "module"
+ },
+ "plugins": ["@typescript-eslint"],
+ "extends": [
+ "eslint:recommended",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "rules": {
+ "@typescript-eslint/no-explicit-any": "warn",
+ "@typescript-eslint/no-unused-vars": [
+ "error",
+ {
+ "argsIgnorePattern": "^_",
+ "varsIgnorePattern": "^_"
+ }
+ ],
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ },
+ "ignorePatterns": ["dist/**", "node_modules/**", "coverage/**", "*.config.js"]
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..9b7fff6
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,50 @@
+name: CI
+
+on:
+ push:
+ branches: [main, working]
+ pull_request:
+ branches: [main, working]
+
+jobs:
+ verify:
+ name: Typecheck, Lint, Test & Package
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [18.x, 20.x]
+
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: 'npm'
+
+ - name: Install Dependencies
+ run: npm ci
+
+ - name: TypeScript Typecheck
+ run: npm run typecheck
+
+ - name: ESLint Check
+ run: npm run lint
+
+ - name: Run Test Suite
+ run: npm test
+
+ - name: Build Extension & CLI Bundles
+ run: npm run build
+
+ - name: Verify CLI Runner & Killer Workflow Demo
+ run: |
+ node dist/cli.js --help
+ npm run demo
+
+ - name: Package VS Code Extension (.vsix)
+ run: npx --yes @vscode/vsce package --no-dependencies
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ad51032
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+node_modules/
+dist/
+out/
+coverage/
+*.vsix
+.DS_Store
+*.log
+.vscode-test/
+.proofcode/report.*
+examples/**/.proofcode/report.*
diff --git a/.proofcode/rules.md b/.proofcode/rules.md
new file mode 100644
index 0000000..0fca269
--- /dev/null
+++ b/.proofcode/rules.md
@@ -0,0 +1,9 @@
+# Project Rules
+
+Define organizational and architectural standards for your codebase. ProofCode evaluates every code change against these rules.
+
+- **Rule 1 (Authentication):** Every API route must authenticate the user.
+- **Rule 2 (Data Privacy):** Never expose passwordHash or secrets in responses.
+- **Rule 3 (Resource Ownership):** Users can only access their own orders or user resources.
+- **Rule 4 (Test Coverage):** All public API endpoints require tests.
+- **Rule 5 (Financial Safety):** Payment operations must be idempotent.
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..25c969c
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,18 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Run ProofCode Extension",
+ "type": "extensionHost",
+ "request": "launch",
+ "args": [
+ "--extensionDevelopmentPath=${workspaceFolder}",
+ "${workspaceFolder}/examples/demo-app"
+ ],
+ "outFiles": [
+ "${workspaceFolder}/dist/**/*.js"
+ ],
+ "preLaunchTask": "npm: build"
+ }
+ ]
+}
diff --git a/.vscodeignore b/.vscodeignore
new file mode 100644
index 0000000..2cabd07
--- /dev/null
+++ b/.vscodeignore
@@ -0,0 +1,16 @@
+.vscode/**
+.vscode-test/**
+src/**
+tests/**
+examples/**
+scripts/**
+coverage/**
+.proofcode/report.*
+.eslintrc.json
+tsconfig.json
+vitest.config.ts
+esbuild.config.js
+.git/**
+.github/**
+**/*.map
+node_modules/**
diff --git a/esbuild.config.js b/esbuild.config.js
new file mode 100644
index 0000000..5a3263a
--- /dev/null
+++ b/esbuild.config.js
@@ -0,0 +1,56 @@
+const esbuild = require('esbuild');
+
+const isProduction = process.argv.includes('--production');
+const isWatch = process.argv.includes('--watch');
+
+/** @type {esbuild.BuildOptions[]} */
+const buildConfigs = [
+ // VS Code Extension Bundle
+ {
+ entryPoints: ['src/extension/index.ts'],
+ bundle: true,
+ outfile: 'dist/extension.js',
+ external: ['vscode'],
+ format: 'cjs',
+ platform: 'node',
+ target: 'node18',
+ sourcemap: !isProduction,
+ minify: isProduction,
+ logLevel: 'info'
+ },
+ // Standalone CLI Bundle
+ {
+ entryPoints: ['src/cli.ts'],
+ bundle: true,
+ outfile: 'dist/cli.js',
+ external: ['vscode'],
+ format: 'cjs',
+ platform: 'node',
+ target: 'node18',
+ banner: {
+ js: '#!/usr/bin/env node\n'
+ },
+ sourcemap: !isProduction,
+ minify: isProduction,
+ logLevel: 'info'
+ }
+];
+
+async function main() {
+ try {
+ for (const config of buildConfigs) {
+ if (isWatch) {
+ const ctx = await esbuild.context(config);
+ await ctx.watch();
+ console.log(`Watching ${config.entryPoints[0]} -> ${config.outfile}`);
+ } else {
+ await esbuild.build(config);
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/examples/demo-app/.proofcode/rules.md b/examples/demo-app/.proofcode/rules.md
new file mode 100644
index 0000000..0fca269
--- /dev/null
+++ b/examples/demo-app/.proofcode/rules.md
@@ -0,0 +1,9 @@
+# Project Rules
+
+Define organizational and architectural standards for your codebase. ProofCode evaluates every code change against these rules.
+
+- **Rule 1 (Authentication):** Every API route must authenticate the user.
+- **Rule 2 (Data Privacy):** Never expose passwordHash or secrets in responses.
+- **Rule 3 (Resource Ownership):** Users can only access their own orders or user resources.
+- **Rule 4 (Test Coverage):** All public API endpoints require tests.
+- **Rule 5 (Financial Safety):** Payment operations must be idempotent.
diff --git a/examples/demo-app/package.json b/examples/demo-app/package.json
new file mode 100644
index 0000000..5824597
--- /dev/null
+++ b/examples/demo-app/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "ecommerce-next-demo",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "test": "echo \"Tests passed\""
+ }
+}
diff --git a/examples/demo-app/src/app/api/auth/login/route.ts b/examples/demo-app/src/app/api/auth/login/route.ts
new file mode 100644
index 0000000..7c2ca53
--- /dev/null
+++ b/examples/demo-app/src/app/api/auth/login/route.ts
@@ -0,0 +1,17 @@
+import { db } from '../../../../lib/db';
+
+export async function POST(req: Request) {
+ const body = await req.json();
+ const username = body.username;
+
+ // Unescaped dynamic SQL query
+ const result = await db.query(`SELECT * FROM users WHERE username = '${username}'`);
+ const user = result.rows[0];
+
+ // Leaks passwordHash in response
+ return Response.json({
+ success: true,
+ user,
+ passwordHash: user ? user.passwordHash : null
+ });
+}
diff --git a/examples/demo-app/src/app/api/orders/[id]/route.ts b/examples/demo-app/src/app/api/orders/[id]/route.ts
new file mode 100644
index 0000000..3037604
--- /dev/null
+++ b/examples/demo-app/src/app/api/orders/[id]/route.ts
@@ -0,0 +1,17 @@
+import { db } from '../../../../lib/db';
+
+/**
+ * Route modified by AI agent:
+ * Fetches order directly from DB without ownership check
+ */
+export async function GET(req: Request) {
+ const url = new URL(req.url);
+ const orderId = url.pathname.split('/').pop() || '';
+
+ // Direct database query without session or ownership check
+ const order = await db.order.findUnique({
+ where: { id: orderId }
+ });
+
+ return Response.json(order);
+}
diff --git a/examples/demo-app/src/components/AdminPanel.tsx b/examples/demo-app/src/components/AdminPanel.tsx
new file mode 100644
index 0000000..5d0d812
--- /dev/null
+++ b/examples/demo-app/src/components/AdminPanel.tsx
@@ -0,0 +1,6 @@
+import { getUser } from '../lib/auth';
+
+export async function renderAdminPanel(adminId: string) {
+ const user = await getUser(adminId);
+ return `
Admin panel for: ${user?.email}
`;
+}
diff --git a/examples/demo-app/src/components/Dashboard.tsx b/examples/demo-app/src/components/Dashboard.tsx
new file mode 100644
index 0000000..34f7bd6
--- /dev/null
+++ b/examples/demo-app/src/components/Dashboard.tsx
@@ -0,0 +1,6 @@
+import { getUser } from '../lib/auth';
+
+export async function renderDashboard(userId: string) {
+ const user = await getUser(userId);
+ return `Welcome, ${user?.email}
`;
+}
diff --git a/examples/demo-app/src/lib/auth.ts b/examples/demo-app/src/lib/auth.ts
new file mode 100644
index 0000000..216abdd
--- /dev/null
+++ b/examples/demo-app/src/lib/auth.ts
@@ -0,0 +1,13 @@
+import { db, User } from './db';
+
+/**
+ * AI modified getUser to add OAuth provider mapping
+ */
+export async function getUser(userId: string): Promise {
+ const user = await db.user.findUnique({ where: { id: userId } });
+ return user;
+}
+
+export async function verifySession(token: string): Promise {
+ return Boolean(token && token.length > 10);
+}
diff --git a/examples/demo-app/src/lib/db.ts b/examples/demo-app/src/lib/db.ts
new file mode 100644
index 0000000..c169064
--- /dev/null
+++ b/examples/demo-app/src/lib/db.ts
@@ -0,0 +1,30 @@
+export interface User {
+ id: string;
+ email: string;
+ passwordHash: string;
+}
+
+export interface Order {
+ id: string;
+ userId: string;
+ amount: number;
+}
+
+export const db = {
+ user: {
+ findUnique: async (query: { where: { id: string } }) => {
+ return { id: query.where.id, email: 'alex@example.com', passwordHash: '$2b$10$e8...' };
+ }
+ },
+ order: {
+ findUnique: async (query: { where: { id: string } }) => {
+ return { id: query.where.id, userId: 'user-123', amount: 99.99 };
+ },
+ findMany: async () => {
+ return [{ id: 'order-1', userId: 'user-123', amount: 49.0 }];
+ }
+ },
+ query: async (sql: string) => {
+ return { rows: [{ id: '1', sql }] };
+ }
+};
diff --git a/examples/demo-app/src/services/orderService.ts b/examples/demo-app/src/services/orderService.ts
new file mode 100644
index 0000000..e8695c7
--- /dev/null
+++ b/examples/demo-app/src/services/orderService.ts
@@ -0,0 +1,8 @@
+import { getUser } from '../lib/auth';
+import { db } from '../lib/db';
+
+export async function getOrderForUser(orderId: string, userId: string) {
+ const user = await getUser(userId);
+ if (!user) throw new Error('User not found');
+ return db.order.findUnique({ where: { id: orderId } });
+}
diff --git a/examples/demo-app/src/services/paymentService.ts b/examples/demo-app/src/services/paymentService.ts
new file mode 100644
index 0000000..f5b358e
--- /dev/null
+++ b/examples/demo-app/src/services/paymentService.ts
@@ -0,0 +1,8 @@
+import { getUser } from '../lib/auth';
+import { getOrderForUser } from './orderService';
+
+export async function processPayment(orderId: string, userId: string) {
+ const user = await getUser(userId);
+ const order = await getOrderForUser(orderId, userId);
+ return { success: true, user, order };
+}
diff --git a/examples/demo-app/tests/auth.test.ts b/examples/demo-app/tests/auth.test.ts
new file mode 100644
index 0000000..4c26268
--- /dev/null
+++ b/examples/demo-app/tests/auth.test.ts
@@ -0,0 +1,9 @@
+import { describe, expect, it } from 'vitest';
+import { getUser } from '../src/lib/auth';
+
+describe('Auth Service', () => {
+ it('should retrieve user', async () => {
+ const user = await getUser('1');
+ expect(user).toBeDefined();
+ });
+});
diff --git a/examples/demo-app/tsconfig.json b/examples/demo-app/tsconfig.json
new file mode 100644
index 0000000..cc5522d
--- /dev/null
+++ b/examples/demo-app/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "es2022",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ }
+ },
+ "include": ["src/**/*", "tests/**/*"]
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..f7b2309
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4877 @@
+{
+ "name": "proofcode",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "proofcode",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "typescript": "~5.5.4"
+ },
+ "bin": {
+ "proofcode": "dist/cli.js"
+ },
+ "devDependencies": {
+ "@types/node": "^20.14.12",
+ "@types/vscode": "^1.85.0",
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
+ "@typescript-eslint/parser": "^7.18.0",
+ "@vitest/coverage-v8": "^2.1.9",
+ "esbuild": "^0.23.1",
+ "eslint": "^8.57.0",
+ "tsx": "^4.23.15",
+ "vitest": "^2.0.5"
+ },
+ "engines": {
+ "vscode": "^1.85.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.9",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz",
+ "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.8"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "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.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
+ "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
+ "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
+ "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
+ "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
+ "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
+ "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
+ "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
+ "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
+ "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
+ "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
+ "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
+ "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
+ "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
+ "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
+ "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
+ "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
+ "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
+ "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
+ "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
+ "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
+ "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/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/@eslint/js": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "deprecated": "Use @eslint/config-array instead",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.3",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz",
+ "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@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/@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/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/lzma-linux-x64-gnu": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^22.20 || ^24.12 || >=25"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.2.tgz",
+ "integrity": "sha512-gjNDUwMswogkw3fNqV00u54uZlgmInuw5EU/1TeqQ6K0bEagoGvqjPdKzy112lKyhe7PkGudZ/kSQHsc9qroIg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.2.tgz",
+ "integrity": "sha512-YkAW8q7p5SCLSaGWaUkmsRnBEb3MGA1pB5nI/8k0lAK5fZsQKRBqkdR1h4UU9p8u3c7ZGdUF2Zcx7mK+E/pUKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.2.tgz",
+ "integrity": "sha512-hNmuIHv+LnrP/+q/44vsmIMWfoh7X2YXOp5FLtjGuvqN52VdtFpOpnshTc1k28tWv1wE/0D5gLjsozBUowEGbQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.2.tgz",
+ "integrity": "sha512-B0t5CoxgF2KL7R5FHAsyN8yVEQCSIkh4XVs8MCDjuBLNjEa8l3zd5tVKaFMv53LfUtHs9p2rnqeavPTOunV+Ww==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.2.tgz",
+ "integrity": "sha512-QQdYnfwrPmc1DoPlddjFvQPzUHFbStKXsHrG6EBYsWHhBVUCjsCRdcfGGXGAvhDpDtTzKK4vy1UrGFJ45rQaiw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.2.tgz",
+ "integrity": "sha512-1xRxtGWif6BB58VSfMavi0Yfs2M4x6CtRp4o7ScY4W7AGlgvf6GSPKdDNdw7TX3tMiQwZY5W/mvyrOJTq/7AKA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.2.tgz",
+ "integrity": "sha512-CXFLEt384BUwwOLR9DURWNHl1bQOVLd1ibOee+yKBJm/fZtvrSAOZ0JbxjAxszVMIovYMwFMS+oP+Q1aPcx8Eg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.2.tgz",
+ "integrity": "sha512-T1dorY0WHm5mMNpMHFl5rYRZ02gLSZcr6J45r66tYb3S3R3DyIRGiGWVdLtF+zOQjratNJaObl9jYumueRiL1A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.2.tgz",
+ "integrity": "sha512-ia6bfaCIc0ldp6bvs9oEkFrJVJKNUMoiDF0+LBZsQAZg+vE0znXnzGqH+ew/FYswE30cruAWPTO3dPw7keaVkQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.2.tgz",
+ "integrity": "sha512-mB/gwfBfXtT0EBpbSssqKCqMFuX8b21Zy2TGoIhehSJEssZ5ksybxymwp1BkEM5Nczh/AAxpmh5q+MkhtundGg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.2.tgz",
+ "integrity": "sha512-VPd0a+XlHoNRI9jIPRHww2UKhDkD6XjbtcKYXZijAnhJS1jLLbiNtVl6a5LyEfNN+eyiyWcJOZXPzBo69oTsew==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.2.tgz",
+ "integrity": "sha512-10N1I6oBpERqzGtHKcCLrEpHlbXSBbMK9TURHvqgWbIFZuU3oJROxJ0H2nHfq96TVI0XJHQ9qzolJMFd2tz0LA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.2.tgz",
+ "integrity": "sha512-dhJrLVZ3wNNhwCmy9/VTg6Pq6J8a8eZ0n15dVRlbSWcDb+8V/1mDfbnvis+vt8W6Pbxzv6gUbTEl4piOT3bbUQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.2.tgz",
+ "integrity": "sha512-tPd2QuwxkTxgju5eARyb3RdgBWWi/O9dn/NFH0VkvGbLzoQeXrH6qMABtW8IODb5lUeBzB3sy2U7qmI4MtQ72g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.2.tgz",
+ "integrity": "sha512-njtDdPvSQWI9CDhIPPxsiEMeHayCfu7wwcbSQ7c2kYPhGVviL+WGVhq6Jfj/b+VuhSTkFuQ0z4ZFbFjkWwBFmg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.2.tgz",
+ "integrity": "sha512-EbOBo3dqCj9ecEV9Z7zvulB6AieMP2ztnKQYGVOSN0pzIyOu+3JH2YJZnLmbPeVvfn6yP6Qi7NbBM27EzGbj2w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.2.tgz",
+ "integrity": "sha512-8wLt2Q5n4qmfiE2Gk8V6KNLSf+oJuBX7Kdw5joFDtnZIZvirS0hLzBc79OiJjV71oYDmBUZLNy6pDgvylo4qiQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.2.tgz",
+ "integrity": "sha512-aIChbkhC4d33bxkVP0LSi2/2lUSGRXE6k4Qa0G7tgpgTOK7kxHTixmx5qC3SKUJ6yUMznHBtQoWaG4QMqLUnhw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.2.tgz",
+ "integrity": "sha512-gQ+TkVZRfjZVcl/UOAxxfWw+sHjzIXoieNwxEyJlD3tRBmBhR8ZDq11w+QEgQDcpslEB+fhN/OEpaTsDoIIHhQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.2.tgz",
+ "integrity": "sha512-sUaJpMKS2JBCvUk8fYimG7aMq2jHVT4kw/uVHxPolKLrMmkKy0KXirmZyZehdNpL7PffqAlPAWUELQcTiD8ChA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.2.tgz",
+ "integrity": "sha512-j1RdJWb0CrlSuwt12OPjLucPlwfLtA/KMOKVvh5rXSJEtl/WDoIGR3CSykK/jLGTo9RqNfW5Ub6kY5Z5ccYl9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.2.tgz",
+ "integrity": "sha512-i3r04aHCIY3eggTBLUp/4IxiBjDpAgQaK89Ue2nMJ2MKuIXX5JfoeZjHw+kPjGGNVDlhi8FT8xzuGMJZe3aehg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.2.tgz",
+ "integrity": "sha512-kCQ15h/uwNMGdN8gjXBZGf+aGE+ODzHfbi90/cAm4+Q89WoMiLODNoj8HoGZmYx87HPxfu5CBNPsZ+ZG4uIAoA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.2.tgz",
+ "integrity": "sha512-yYAuySSs+0X2gSMJPw/Q4jHsHZHyMq1dpAE+XnN790Jg1WuReUAD/+j+e4vuDvS4PXJ4PA4NCzTR4OYtDf18/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.2.tgz",
+ "integrity": "sha512-JY1rQdWOJrKutORqKO1uzrHXV86jFiQzfLFKx2s8S1CjLXL9fC5LOIvk0q7Z7PXJvsByt+sdoE1Q2a9gBr4gLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.43",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
+ "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/vscode": {
+ "version": "1.140.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.140.0.tgz",
+ "integrity": "sha512-KAB5RBZIgnUhpp4mKsVJPPHLOQKu6WtGy2xpfWjaajEXO4xIIC+UESAXLiQ9ewx6QD1uBmvC/S8BJzkbvv4bRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
+ "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "7.18.0",
+ "@typescript-eslint/type-utils": "7.18.0",
+ "@typescript-eslint/utils": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^7.0.0",
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz",
+ "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "7.18.0",
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/typescript-estree": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
+ "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz",
+ "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "7.18.0",
+ "@typescript-eslint/utils": "7.18.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
+ "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
+ "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/visitor-keys": "7.18.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
+ "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "7.18.0",
+ "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/typescript-estree": "7.18.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
+ "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "7.18.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz",
+ "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@vitest/coverage-v8": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz",
+ "integrity": "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "@bcoe/v8-coverage": "^0.2.3",
+ "debug": "^4.3.7",
+ "istanbul-lib-coverage": "^3.2.2",
+ "istanbul-lib-report": "^3.0.1",
+ "istanbul-lib-source-maps": "^5.0.6",
+ "istanbul-reports": "^3.1.7",
+ "magic-string": "^0.30.12",
+ "magicast": "^0.3.5",
+ "std-env": "^3.8.0",
+ "test-exclude": "^7.0.1",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@vitest/browser": "2.1.9",
+ "vitest": "2.1.9"
+ },
+ "peerDependenciesMeta": {
+ "@vitest/browser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
+ "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "2.1.9",
+ "@vitest/utils": "2.1.9",
+ "chai": "^5.1.2",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
+ "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "2.1.9",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.12"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
+ "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
+ "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "2.1.9",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
+ "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.9",
+ "magic-string": "^0.30.12",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
+ "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyspy": "^3.0.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
+ "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.9",
+ "loupe": "^3.1.2",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz",
+ "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "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/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/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/brace-expansion": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz",
+ "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "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/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chai": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
+ "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/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/check-error": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
+ "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/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/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/deep-eql": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
+ "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.23.1",
+ "@esbuild/android-arm": "0.23.1",
+ "@esbuild/android-arm64": "0.23.1",
+ "@esbuild/android-x64": "0.23.1",
+ "@esbuild/darwin-arm64": "0.23.1",
+ "@esbuild/darwin-x64": "0.23.1",
+ "@esbuild/freebsd-arm64": "0.23.1",
+ "@esbuild/freebsd-x64": "0.23.1",
+ "@esbuild/linux-arm": "0.23.1",
+ "@esbuild/linux-arm64": "0.23.1",
+ "@esbuild/linux-ia32": "0.23.1",
+ "@esbuild/linux-loong64": "0.23.1",
+ "@esbuild/linux-mips64el": "0.23.1",
+ "@esbuild/linux-ppc64": "0.23.1",
+ "@esbuild/linux-riscv64": "0.23.1",
+ "@esbuild/linux-s390x": "0.23.1",
+ "@esbuild/linux-x64": "0.23.1",
+ "@esbuild/netbsd-x64": "0.23.1",
+ "@esbuild/openbsd-arm64": "0.23.1",
+ "@esbuild/openbsd-x64": "0.23.1",
+ "@esbuild/sunos-x64": "0.23.1",
+ "@esbuild/win32-arm64": "0.23.1",
+ "@esbuild/win32-ia32": "0.23.1",
+ "@esbuild/win32-x64": "0.23.1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/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/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
+ "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "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/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz",
+ "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "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": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz",
+ "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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/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/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/glob/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/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/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/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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-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": "5.0.6",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
+ "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.23",
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0"
+ },
+ "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/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loupe": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
+ "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/magicast": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz",
+ "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.25.4",
+ "@babel/types": "^7.25.4",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "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/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "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/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.20",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
+ "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/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/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/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/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-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pathval": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+ "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.16"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "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/postcss": {
+ "version": "8.5.29",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz",
+ "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.19",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.64.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.2.tgz",
+ "integrity": "sha512-RxXm0XVVV1hFI3dYe12ruaOgsYQ725lUcUWeMSQqkF9tTxVErY+fy2Ets6vWhItL9MZMSNZGbvZFc7rH/VHp2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+ "@rollup/rollup-android-arm-eabi": "4.64.2",
+ "@rollup/rollup-android-arm64": "4.64.2",
+ "@rollup/rollup-darwin-arm64": "4.64.2",
+ "@rollup/rollup-darwin-x64": "4.64.2",
+ "@rollup/rollup-freebsd-arm64": "4.64.2",
+ "@rollup/rollup-freebsd-x64": "4.64.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.64.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.64.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.64.2",
+ "@rollup/rollup-linux-arm64-musl": "4.64.2",
+ "@rollup/rollup-linux-loong64-gnu": "4.64.2",
+ "@rollup/rollup-linux-loong64-musl": "4.64.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.64.2",
+ "@rollup/rollup-linux-ppc64-musl": "4.64.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.64.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.64.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.64.2",
+ "@rollup/rollup-linux-x64-gnu": "4.64.2",
+ "@rollup/rollup-linux-x64-musl": "4.64.2",
+ "@rollup/rollup-openbsd-x64": "4.64.2",
+ "@rollup/rollup-openharmony-arm64": "4.64.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.64.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.64.2",
+ "@rollup/rollup-win32-x64-gnu": "4.64.2",
+ "@rollup/rollup-win32-x64-msvc": "4.64.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/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-js": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
+ "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "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/string-width-cjs/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/string-width/node_modules/ansi-regex": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz",
+ "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-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-ansi-cjs": {
+ "name": "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-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/test-exclude": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz",
+ "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^10.4.1",
+ "minimatch": "^10.2.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/test-exclude/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/test-exclude/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "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": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/test-exclude/node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/test-exclude/node_modules/minimatch": {
+ "version": "10.2.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
+ "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.8"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/test-exclude/node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "5.0.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
+ "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinypool": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+ "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+ "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tinyspy": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+ "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "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-api-utils": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
+ "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/tsx": {
+ "version": "4.23.15",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz",
+ "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.28.0"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-node": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
+ "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.7",
+ "es-module-lexer": "^1.5.4",
+ "pathe": "^1.1.2",
+ "vite": "^5.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
+ "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "2.1.9",
+ "@vitest/mocker": "2.1.9",
+ "@vitest/pretty-format": "^2.1.9",
+ "@vitest/runner": "2.1.9",
+ "@vitest/snapshot": "2.1.9",
+ "@vitest/spy": "2.1.9",
+ "@vitest/utils": "2.1.9",
+ "chai": "^5.1.2",
+ "debug": "^4.3.7",
+ "expect-type": "^1.1.0",
+ "magic-string": "^0.30.12",
+ "pathe": "^1.1.2",
+ "std-env": "^3.8.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^0.3.1",
+ "tinypool": "^1.0.1",
+ "tinyrainbow": "^1.2.0",
+ "vite": "^5.0.0",
+ "vite-node": "2.1.9",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "@vitest/browser": "2.1.9",
+ "@vitest/ui": "2.1.9",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "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/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz",
+ "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/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/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
new file mode 100644
index 0000000..2c4581c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,169 @@
+{
+ "name": "proofcode",
+ "displayName": "ProofCode - Code Change Verification",
+ "description": "AI writes code. ProofCode proves the change. Verify impact, detect risks with evidence, and check project rules before shipping.",
+ "version": "0.1.0",
+ "publisher": "proofcode",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/waleedcodes/proofcode.git"
+ },
+ "bugs": {
+ "url": "https://github.com/waleedcodes/proofcode/issues"
+ },
+ "homepage": "https://github.com/waleedcodes/proofcode#readme",
+ "engines": {
+ "vscode": "^1.85.0"
+ },
+ "categories": [
+ "Programming Languages",
+ "Testing",
+ "Linters",
+ "Other"
+ ],
+ "keywords": [
+ "verification",
+ "impact-analysis",
+ "ai-code",
+ "code-review",
+ "security",
+ "git-diff"
+ ],
+ "main": "./dist/extension.js",
+ "bin": {
+ "proofcode": "./dist/cli.js"
+ },
+ "activationEvents": [
+ "onStartupFinished"
+ ],
+ "contributes": {
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "proofcode-explorer",
+ "title": "ProofCode",
+ "icon": "resources/icon.svg"
+ }
+ ]
+ },
+ "views": {
+ "proofcode-explorer": [
+ {
+ "id": "proofcode.views.overview",
+ "name": "Verification Overview",
+ "type": "tree"
+ }
+ ]
+ },
+ "commands": [
+ {
+ "command": "proofcode.verifyChange",
+ "title": "Verify Current Changes",
+ "category": "ProofCode",
+ "icon": "$(shield)"
+ },
+ {
+ "command": "proofcode.openDashboard",
+ "title": "Open Verification Dashboard",
+ "category": "ProofCode",
+ "icon": "$(dashboard)"
+ },
+ {
+ "command": "proofcode.initRules",
+ "title": "Initialize .proofcode/rules.md",
+ "category": "ProofCode",
+ "icon": "$(file-code)"
+ },
+ {
+ "command": "proofcode.openEvidence",
+ "title": "Open Evidence Location",
+ "category": "ProofCode"
+ },
+ {
+ "command": "proofcode.exportReport",
+ "title": "Export Verification Report",
+ "category": "ProofCode",
+ "icon": "$(desktop-download)"
+ }
+ ],
+ "menus": {
+ "view/title": [
+ {
+ "command": "proofcode.verifyChange",
+ "when": "view == proofcode.views.overview",
+ "group": "navigation@1"
+ },
+ {
+ "command": "proofcode.openDashboard",
+ "when": "view == proofcode.views.overview",
+ "group": "navigation@2"
+ }
+ ],
+ "commandPalette": [
+ {
+ "command": "proofcode.verifyChange"
+ },
+ {
+ "command": "proofcode.openDashboard"
+ },
+ {
+ "command": "proofcode.initRules"
+ },
+ {
+ "command": "proofcode.exportReport"
+ }
+ ]
+ },
+ "configuration": {
+ "title": "ProofCode",
+ "properties": {
+ "proofcode.autoVerifyOnSave": {
+ "type": "boolean",
+ "default": true,
+ "description": "Automatically update impact and risk analysis when files are saved."
+ },
+ "proofcode.runTestsOnVerify": {
+ "type": "boolean",
+ "default": false,
+ "description": "Automatically run project tests when verifying changes."
+ },
+ "proofcode.rulesFilePath": {
+ "type": "string",
+ "default": ".proofcode/rules.md",
+ "description": "Path to the project rules markdown file."
+ }
+ }
+ }
+ },
+ "scripts": {
+ "vscode:prepublish": "npm run build",
+ "compile": "tsc -p ./ --noEmit",
+ "build": "node esbuild.config.js --production",
+ "build:dev": "node esbuild.config.js",
+ "watch": "node esbuild.config.js --watch",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:coverage": "vitest run --coverage",
+ "lint": "eslint src --ext .ts",
+ "lint:fix": "eslint src --ext .ts --fix",
+ "typecheck": "tsc --noEmit",
+ "cli": "node dist/cli.js",
+ "demo": "tsx scripts/run-demo.ts",
+ "package": "npx @vscode/vsce package --no-dependencies"
+ },
+ "dependencies": {
+ "typescript": "~5.5.4"
+ },
+ "devDependencies": {
+ "@types/node": "^20.14.12",
+ "@types/vscode": "^1.85.0",
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
+ "@typescript-eslint/parser": "^7.18.0",
+ "@vitest/coverage-v8": "^2.1.9",
+ "esbuild": "^0.23.1",
+ "eslint": "^8.57.0",
+ "tsx": "^4.23.15",
+ "vitest": "^2.0.5"
+ }
+}
diff --git a/resources/icon.svg b/resources/icon.svg
new file mode 100644
index 0000000..2011c97
--- /dev/null
+++ b/resources/icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/scripts/run-demo.ts b/scripts/run-demo.ts
new file mode 100644
index 0000000..c0808ef
--- /dev/null
+++ b/scripts/run-demo.ts
@@ -0,0 +1,33 @@
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import { ProofCodeEngine } from '../src/engine/proofCodeEngine';
+import { ReportFormatter } from '../src/engine/reportFormatter';
+
+async function main() {
+ const demoRoot = path.resolve(__dirname, '../examples/demo-app');
+ console.log('\n===============================================================');
+ console.log(' 🚀 PROOFCODE KILLER WORKFLOW DEMO (Next.js AI Change) ');
+ console.log('===============================================================');
+ console.log(`Analyzing codebase at: ${demoRoot}\n`);
+
+ const engine = new ProofCodeEngine(demoRoot);
+ const report = await engine.verify();
+
+ console.log(ReportFormatter.toTerminal(report));
+
+ // Write demo report
+ const reportDir = path.join(demoRoot, '.proofcode');
+ if (!fs.existsSync(reportDir)) {
+ fs.mkdirSync(reportDir, { recursive: true });
+ }
+
+ const reportPath = path.join(reportDir, 'report.md');
+ fs.writeFileSync(reportPath, ReportFormatter.toMarkdown(report), 'utf8');
+
+ console.log(`\n📄 Generated Markdown Verification Report at:\n${reportPath}\n`);
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/src/analyzer/astAnalyzer.ts b/src/analyzer/astAnalyzer.ts
new file mode 100644
index 0000000..97575e6
--- /dev/null
+++ b/src/analyzer/astAnalyzer.ts
@@ -0,0 +1,328 @@
+import * as fs from 'node:fs';
+import * as ts from 'typescript';
+import { ExportInfo, FileAnalysis, ImportInfo, SymbolInfo, SymbolKind } from '../types';
+
+export class AstAnalyzer {
+ /**
+ * Analyzes a TypeScript/JavaScript source file and extracts symbols, imports, exports, and API routes.
+ */
+ public static analyzeFile(filePath: string, content?: string): FileAnalysis {
+ const sourceText = content ?? (fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '');
+ const isJsx = filePath.endsWith('.tsx') || filePath.endsWith('.jsx');
+ const sourceFile = ts.createSourceFile(
+ filePath,
+ sourceText,
+ ts.ScriptTarget.Latest,
+ true,
+ isJsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS
+ );
+
+ const symbols: SymbolInfo[] = [];
+ const imports: ImportInfo[] = [];
+ const exports: ExportInfo[] = [];
+ const apiRoutes: FileAnalysis['apiRoutes'] = [];
+
+ const isNextAppRoute = /[\\/]app[\\/].*[\\/]route\.(?:ts|js|tsx|jsx)$/.test(filePath);
+ const isNextPagesApiRoute = /[\\/]pages[\\/]api[\\/].*\.(?:ts|js|tsx|jsx)$/.test(filePath);
+
+ const getLineAndColumn = (pos: number) => {
+ const lc = sourceFile.getLineAndCharacterOfPosition(pos);
+ return { line: lc.line + 1, column: lc.character + 1 };
+ };
+
+ const extractCallsFromNode = (node: ts.Node): string[] => {
+ const calls: string[] = [];
+ const visitCall = (n: ts.Node) => {
+ if (ts.isCallExpression(n)) {
+ const expression = n.expression;
+ if (ts.isIdentifier(expression)) {
+ calls.push(expression.text);
+ } else if (ts.isPropertyAccessExpression(expression)) {
+ calls.push(expression.getText(sourceFile));
+ calls.push(expression.name.text);
+ }
+ }
+ ts.forEachChild(n, visitCall);
+ };
+ ts.forEachChild(node, visitCall);
+ return Array.from(new Set(calls));
+ };
+
+ const visit = (node: ts.Node) => {
+ // 1. Imports
+ if (ts.isImportDeclaration(node)) {
+ const source = (node.moduleSpecifier as ts.StringLiteral).text;
+ const line = getLineAndColumn(node.getStart(sourceFile)).line;
+ const specifiers: ImportInfo['specifiers'] = [];
+
+ if (node.importClause) {
+ if (node.importClause.name) {
+ specifiers.push({
+ name: node.importClause.name.text,
+ isDefault: true
+ });
+ }
+ if (node.importClause.namedBindings) {
+ if (ts.isNamespaceImport(node.importClause.namedBindings)) {
+ specifiers.push({
+ name: node.importClause.namedBindings.name.text,
+ isNamespace: true
+ });
+ } else if (ts.isNamedImports(node.importClause.namedBindings)) {
+ for (const element of node.importClause.namedBindings.elements) {
+ specifiers.push({
+ name: element.name.text,
+ propertyName: element.propertyName?.text
+ });
+ }
+ }
+ }
+ }
+
+ imports.push({ source, specifiers, line });
+ }
+
+ // 2. Export Declarations (e.g. export { foo, bar }; export * from './foo')
+ if (ts.isExportDeclaration(node)) {
+ const line = getLineAndColumn(node.getStart(sourceFile)).line;
+ if (node.exportClause && ts.isNamedExports(node.exportClause)) {
+ for (const el of node.exportClause.elements) {
+ exports.push({
+ name: el.name.text,
+ isDefault: false,
+ line
+ });
+ }
+ }
+ }
+
+ // 3. Functions
+ if (ts.isFunctionDeclaration(node) && node.name) {
+ const name = node.name.text;
+ const start = getLineAndColumn(node.getStart(sourceFile));
+ const end = getLineAndColumn(node.getEnd());
+ const isExported = Boolean(
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
+ );
+ const isDefault = Boolean(
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)
+ );
+ const calls = extractCallsFromNode(node);
+
+ const sym: SymbolInfo = {
+ name,
+ kind: 'function',
+ filePath,
+ startLine: start.line,
+ endLine: end.line,
+ startColumn: start.column,
+ endColumn: end.column,
+ isExported,
+ isDefaultExport: isDefault,
+ calls
+ };
+ symbols.push(sym);
+
+ if (isExported) {
+ exports.push({ name, isDefault, line: start.line });
+ if (isNextAppRoute && /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/i.test(name)) {
+ apiRoutes.push({
+ method: name.toUpperCase(),
+ path: filePath,
+ handlerSymbol: name,
+ line: start.line
+ });
+ }
+ }
+ }
+
+ // 4. Variable statements (const foo = () => {}, const bar = function() {})
+ if (ts.isVariableStatement(node)) {
+ const isTopLevel = node.parent === sourceFile;
+ const isExported = Boolean(
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
+ );
+
+ if (isTopLevel) {
+ for (const decl of node.declarationList.declarations) {
+ if (ts.isIdentifier(decl.name)) {
+ const name = decl.name.text;
+ const start = getLineAndColumn(decl.getStart(sourceFile));
+ const end = getLineAndColumn(decl.getEnd());
+ let kind: SymbolKind = 'variable';
+
+ if (decl.initializer) {
+ if (ts.isArrowFunction(decl.initializer)) {
+ kind = 'arrow_function';
+ } else if (ts.isFunctionExpression(decl.initializer)) {
+ kind = 'function';
+ }
+ }
+
+ const calls = decl.initializer ? extractCallsFromNode(decl.initializer) : [];
+
+ symbols.push({
+ name,
+ kind,
+ filePath,
+ startLine: start.line,
+ endLine: end.line,
+ startColumn: start.column,
+ endColumn: end.column,
+ isExported,
+ calls
+ });
+
+ if (isExported) {
+ exports.push({ name, isDefault: false, line: start.line });
+ if (isNextAppRoute && /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/i.test(name)) {
+ apiRoutes.push({
+ method: name.toUpperCase(),
+ path: filePath,
+ handlerSymbol: name,
+ line: start.line
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // 5. Classes & Methods
+ if (ts.isClassDeclaration(node) && node.name) {
+ const className = node.name.text;
+ const start = getLineAndColumn(node.getStart(sourceFile));
+ const end = getLineAndColumn(node.getEnd());
+ const isExported = Boolean(
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
+ );
+ const isDefault = Boolean(
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)
+ );
+
+ symbols.push({
+ name: className,
+ kind: 'class',
+ filePath,
+ startLine: start.line,
+ endLine: end.line,
+ startColumn: start.column,
+ endColumn: end.column,
+ isExported,
+ isDefaultExport: isDefault,
+ calls: []
+ });
+
+ if (isExported) {
+ exports.push({ name: className, isDefault, line: start.line });
+ }
+
+ // Methods within class
+ for (const member of node.members) {
+ if (ts.isMethodDeclaration(member) && member.name && ts.isIdentifier(member.name)) {
+ const mName = member.name.text;
+ const mStart = getLineAndColumn(member.getStart(sourceFile));
+ const mEnd = getLineAndColumn(member.getEnd());
+ const mCalls = extractCallsFromNode(member);
+
+ symbols.push({
+ name: `${className}.${mName}`,
+ kind: 'method',
+ filePath,
+ startLine: mStart.line,
+ endLine: mEnd.line,
+ startColumn: mStart.column,
+ endColumn: mEnd.column,
+ isExported,
+ parentSymbol: className,
+ calls: mCalls
+ });
+ }
+ }
+ }
+
+ // 6. Interfaces & Type Aliases
+ if (ts.isInterfaceDeclaration(node)) {
+ const name = node.name.text;
+ const start = getLineAndColumn(node.getStart(sourceFile));
+ const end = getLineAndColumn(node.getEnd());
+ const isExported = Boolean(
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
+ );
+
+ symbols.push({
+ name,
+ kind: 'interface',
+ filePath,
+ startLine: start.line,
+ endLine: end.line,
+ startColumn: start.column,
+ endColumn: end.column,
+ isExported,
+ calls: []
+ });
+ }
+
+ if (ts.isTypeAliasDeclaration(node)) {
+ const name = node.name.text;
+ const start = getLineAndColumn(node.getStart(sourceFile));
+ const end = getLineAndColumn(node.getEnd());
+ const isExported = Boolean(
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
+ );
+
+ symbols.push({
+ name,
+ kind: 'type_alias',
+ filePath,
+ startLine: start.line,
+ endLine: end.line,
+ startColumn: start.column,
+ endColumn: end.column,
+ isExported,
+ calls: []
+ });
+ }
+
+ ts.forEachChild(node, visit);
+ };
+
+ ts.forEachChild(sourceFile, visit);
+
+ if (isNextPagesApiRoute && apiRoutes.length === 0) {
+ apiRoutes.push({
+ method: 'ALL',
+ path: filePath,
+ handlerSymbol: 'default',
+ line: 1
+ });
+ }
+
+ return {
+ filePath,
+ symbols,
+ imports,
+ exports,
+ hasApiRoute: apiRoutes.length > 0,
+ apiRoutes
+ };
+ }
+
+ /**
+ * Filters symbols that intersect with the provided modified line numbers.
+ */
+ public static findSymbolsAtLines(symbols: SymbolInfo[], lineNumbers: number[]): SymbolInfo[] {
+ if (lineNumbers.length === 0) return [];
+ const lineSet = new Set(lineNumbers);
+
+ return symbols.filter((sym) => {
+ for (let l = sym.startLine; l <= sym.endLine; l++) {
+ if (lineSet.has(l)) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+}
diff --git a/src/cli.ts b/src/cli.ts
new file mode 100644
index 0000000..e3f8a8d
--- /dev/null
+++ b/src/cli.ts
@@ -0,0 +1,102 @@
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import { ProofCodeEngine } from './engine/proofCodeEngine';
+import { ReportFormatter } from './engine/reportFormatter';
+import { ProjectRulesEngine } from './rules/projectRulesEngine';
+
+async function runCli(): Promise {
+ const args = process.argv.slice(2);
+ const cwd = process.cwd();
+
+ const isHelp = args.includes('--help') || args.includes('-h');
+ const isJson = args.includes('--json');
+ const isMarkdown = args.includes('--markdown') || args.includes('-m');
+ const isStaged = args.includes('--staged');
+ const isLive = args.includes('--live');
+ const command = args[0] && !args[0].startsWith('-') ? args[0] : 'verify';
+
+ if (isHelp) {
+ console.log(`
+ProofCode CLI - Code Change Verification
+AI writes code. ProofCode proves the change.
+
+Usage:
+ proofcode [command] [options]
+
+Commands:
+ verify Verify working tree or git diff (default)
+ init Initialize default .proofcode/rules.md in project
+ rules List and check configured project rules
+
+Options:
+ --staged Verify only staged git changes
+ --live Run full live test and build checks (npm test, tsc, etc.)
+ --json Output report as structured JSON
+ --markdown Output report as formatted GitHub Markdown
+ --help, -h Show this help message
+`);
+ process.exit(0);
+ }
+
+ if (command === 'init') {
+ const rulesEngine = new ProjectRulesEngine(cwd);
+ const created = await rulesEngine.initDefaultRulesFile();
+ console.log(`Initialized ProofCode rules at: ${created}`);
+ process.exit(0);
+ }
+
+ if (command === 'rules') {
+ const rulesEngine = new ProjectRulesEngine(cwd);
+ const rules = rulesEngine.parseRules();
+ console.log(`\nConfigured Rules (${rules.length}):`);
+ rules.forEach((r, idx) => {
+ console.log(` ${idx + 1}. [${r.severity}] ${r.title}: ${r.description}`);
+ });
+ console.log('');
+ process.exit(0);
+ }
+
+ // Default: Verify
+ try {
+ const engine = new ProofCodeEngine(cwd);
+ const report = await engine.verify({
+ staged: isStaged,
+ runLiveTests: isLive
+ });
+
+ if (isJson) {
+ console.log(ReportFormatter.toJson(report));
+ } else if (isMarkdown) {
+ console.log(ReportFormatter.toMarkdown(report));
+ } else {
+ console.log(ReportFormatter.toTerminal(report));
+ }
+
+ // Write artifact report if .proofcode dir exists
+ const proofcodeDir = path.join(cwd, '.proofcode');
+ if (fs.existsSync(proofcodeDir)) {
+ fs.writeFileSync(
+ path.join(proofcodeDir, 'report.md'),
+ ReportFormatter.toMarkdown(report),
+ 'utf8'
+ );
+ fs.writeFileSync(
+ path.join(proofcodeDir, 'report.json'),
+ ReportFormatter.toJson(report),
+ 'utf8'
+ );
+ }
+
+ // Exit code: 0 if ready or needs review, 1 if blocked
+ if (report.verdict === 'BLOCKED') {
+ process.exit(1);
+ } else {
+ process.exit(0);
+ }
+ } catch (err: unknown) {
+ console.error(`Verification failed: ${(err as Error).message}`);
+ process.exit(1);
+ }
+}
+
+runCli();
diff --git a/src/engine/proofCodeEngine.ts b/src/engine/proofCodeEngine.ts
new file mode 100644
index 0000000..a21e5f4
--- /dev/null
+++ b/src/engine/proofCodeEngine.ts
@@ -0,0 +1,399 @@
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import { GitService } from '../git/gitService';
+import { ProjectGraph } from '../graph/projectGraph';
+import { ProjectRulesEngine } from '../rules/projectRulesEngine';
+import { RiskEngine } from '../rules/riskEngine';
+import { TestRunner } from '../test-runner/testRunner';
+import {
+ BreakageRiskItem,
+ CheckResult,
+ FileDiff,
+ ImpactAnalysis,
+ RiskFinding,
+ RuleResult,
+ VerificationReport,
+ VerificationScore,
+ VerificationVerdict
+} from '../types';
+
+export interface VerifyOptions {
+ staged?: boolean;
+ baseRef?: string;
+ runLiveTests?: boolean;
+ rulesFilePath?: string;
+}
+
+export class ProofCodeEngine {
+ private workspaceRoot: string;
+ private gitService: GitService;
+ private testRunner: TestRunner;
+
+ constructor(workspaceRoot: string) {
+ this.workspaceRoot = workspaceRoot;
+ this.gitService = new GitService({ cwd: workspaceRoot });
+ this.testRunner = new TestRunner();
+ }
+
+ public getWorkspaceRoot(): string {
+ return this.workspaceRoot;
+ }
+
+ public async verify(options: VerifyOptions = {}): Promise {
+ const repoRoot = await this.gitService.getRepoRoot();
+ const branch = await this.gitService.getCurrentBranch(repoRoot);
+
+ // 1. Git Diff & Change Extraction
+ const diffResult = await this.gitService.getDiff({
+ staged: options.staged,
+ baseRef: options.baseRef,
+ repoRoot
+ });
+
+ // If workspaceRoot is a subfolder of repoRoot, scope verification to workspaceRoot
+ const targetWorkspace = path.resolve(this.workspaceRoot);
+ const isSubfolder =
+ targetWorkspace !== path.resolve(repoRoot) &&
+ targetWorkspace.startsWith(path.resolve(repoRoot));
+
+ let filesToVerify = isSubfolder
+ ? diffResult.files.filter((f) => {
+ const full = path.resolve(repoRoot, f.newPath);
+ return full.startsWith(targetWorkspace);
+ })
+ : diffResult.files;
+
+ const activeRoot = isSubfolder ? targetWorkspace : repoRoot;
+
+ // If verifying a specific subfolder workspace (e.g. demo fixture or sub-package)
+ // and there are no active git diffs, synthesize diff files covering all source files
+ if (filesToVerify.length === 0 && isSubfolder) {
+ const graphHelper = new ProjectGraph(activeRoot);
+ const allFiles = graphHelper.findSourceFiles(activeRoot);
+ filesToVerify = allFiles.map((fp: string) => {
+ const content = fs.existsSync(fp) ? fs.readFileSync(fp, 'utf8') : '';
+ const lineCount = content.split('\n').length;
+ const lines: number[] = [];
+ for (let i = 1; i <= lineCount; i++) lines.push(i);
+ return {
+ oldPath: path.relative(repoRoot, fp),
+ newPath: path.relative(repoRoot, fp),
+ isNew: false,
+ isDeleted: false,
+ isRenamed: false,
+ status: 'modified' as const,
+ addedLines: lines,
+ deletedLines: [],
+ hunks: [],
+ modifiedLineRanges: [{ start: 1, end: lineCount }]
+ };
+ });
+ }
+
+ // 2. Project Graph & Dependency Mapping
+ const graph = new ProjectGraph(activeRoot);
+ await graph.buildGraph();
+
+ // Map changed files to added/changed lines
+ const fileChangedLines = new Map();
+ for (const file of filesToVerify) {
+ const fullPath = path.resolve(repoRoot, file.newPath);
+ fileChangedLines.set(fullPath, file.addedLines);
+ }
+
+ // 3. Compute Change Impact
+ const impact = graph.computeImpact(fileChangedLines);
+
+ // 4. Static Risk Analysis on changed files
+ const allRisks: RiskFinding[] = [];
+ for (const file of filesToVerify) {
+ const fullPath = path.resolve(repoRoot, file.newPath);
+ const risks = RiskEngine.analyzeFile(fullPath, undefined, file);
+ allRisks.push(...risks);
+ }
+
+ // 5. Project Rules Verification
+ const rulesEngine = new ProjectRulesEngine(activeRoot, options.rulesFilePath);
+ const ruleResults = await rulesEngine.evaluateRules(filesToVerify, graph);
+
+ // 6. Test Runner Checks
+ let tsCheck: CheckResult = { name: 'TypeScript', status: 'PASS', message: 'Ready' };
+ let lintCheck: CheckResult = { name: 'ESLint', status: 'PASS', message: 'Ready' };
+ let unitTestsCheck: CheckResult = { name: 'Unit Tests', status: 'SKIPPED', message: 'Not run' };
+ let buildCheck: CheckResult = { name: 'Build', status: 'SKIPPED', message: 'Not run' };
+
+ if (options.runLiveTests) {
+ [tsCheck, lintCheck, unitTestsCheck, buildCheck] = await Promise.all([
+ this.testRunner.runTypeScriptCheck(repoRoot),
+ this.testRunner.runLint(repoRoot),
+ this.testRunner.runUnitTests(repoRoot),
+ this.testRunner.runBuild(repoRoot)
+ ]);
+ }
+
+ // 7. Calculate Verification Score
+ const score = this.calculateVerificationScore({
+ filesChanged: filesToVerify,
+ risks: allRisks,
+ rules: ruleResults,
+ untestedCallersCount: impact.untestedCallersCount,
+ checks: { tsCheck, lintCheck, unitTestsCheck, buildCheck }
+ });
+
+ // 8. Determine Verdict
+ const verdict = this.determineVerdict(score.total, allRisks, ruleResults, {
+ tsCheck,
+ lintCheck,
+ unitTestsCheck
+ });
+
+ // Count test files touched
+ const testsAdded = filesToVerify.filter(
+ (f) => f.isNew && graph.isTestFile(f.newPath)
+ ).length;
+ const testsAffected = filesToVerify.filter((f) => graph.isTestFile(f.newPath)).length;
+
+ const highRiskCount = allRisks.filter((r) => r.severity === 'HIGH').length;
+ const mediumRiskCount = allRisks.filter((r) => r.severity === 'MEDIUM').length;
+ const lowRiskCount = allRisks.filter((r) => r.severity === 'LOW').length;
+
+ const totalLinesAdded = filesToVerify.reduce((acc, f) => acc + f.addedLines.length, 0);
+ const totalLinesDeleted = filesToVerify.reduce((acc, f) => acc + f.deletedLines.length, 0);
+
+ const breakageRisks = this.computeBreakageRisks(impact, allRisks, ruleResults);
+
+ return {
+ timestamp: new Date().toISOString(),
+ repoRoot: activeRoot,
+ branch,
+ score,
+ verdict,
+ summary: {
+ filesChanged: filesToVerify.length,
+ linesAdded: totalLinesAdded,
+ linesDeleted: totalLinesDeleted,
+ symbolsAffected: impact.changedSymbols.length,
+ apiRoutesAffected: impact.affectedRoutes.length,
+ testsAdded,
+ testsAffected,
+ highRiskCount,
+ mediumRiskCount,
+ lowRiskCount
+ },
+ impact,
+ risks: allRisks,
+ breakageRisks,
+ rules: ruleResults,
+ checks: {
+ typescript: tsCheck,
+ eslint: lintCheck,
+ unitTests: unitTestsCheck,
+ build: buildCheck
+ }
+ };
+ }
+
+ private calculateVerificationScore(params: {
+ filesChanged: FileDiff[];
+ risks: RiskFinding[];
+ rules: { status: string }[];
+ untestedCallersCount: number;
+ checks: {
+ tsCheck: CheckResult;
+ lintCheck: CheckResult;
+ unitTestsCheck: CheckResult;
+ buildCheck: CheckResult;
+ };
+ }): VerificationScore {
+ let currentScore = 100;
+ const deductions: VerificationScore['deductions'] = [];
+
+ // Deduct for High Risks (15 pts each, max 45)
+ const highRisks = params.risks.filter((r) => r.severity === 'HIGH').length;
+ if (highRisks > 0) {
+ const deduction = Math.min(45, highRisks * 15);
+ currentScore -= deduction;
+ deductions.push({
+ reason: `${highRisks} High Severity Risk(s) detected`,
+ amount: deduction
+ });
+ }
+
+ // Deduct for Medium Risks (7 pts each, max 28)
+ const medRisks = params.risks.filter((r) => r.severity === 'MEDIUM').length;
+ if (medRisks > 0) {
+ const deduction = Math.min(28, medRisks * 7);
+ currentScore -= deduction;
+ deductions.push({
+ reason: `${medRisks} Medium Severity Risk(s) detected`,
+ amount: deduction
+ });
+ }
+
+ // Deduct for Low Risks (2 pts each, max 10)
+ const lowRisks = params.risks.filter((r) => r.severity === 'LOW').length;
+ if (lowRisks > 0) {
+ const deduction = Math.min(10, lowRisks * 2);
+ currentScore -= deduction;
+ deductions.push({
+ reason: `${lowRisks} Low Severity Risk(s) detected`,
+ amount: deduction
+ });
+ }
+
+ // Deduct for Project Rule Violations (15 pts each)
+ const violations = params.rules.filter((r) => r.status === 'VIOLATION').length;
+ if (violations > 0) {
+ const deduction = Math.min(30, violations * 15);
+ currentScore -= deduction;
+ deductions.push({
+ reason: `${violations} Project Rule Violation(s)`,
+ amount: deduction
+ });
+ }
+
+ // Deduct for Rule Warnings (5 pts each)
+ const warnings = params.rules.filter((r) => r.status === 'WARNING').length;
+ if (warnings > 0) {
+ const deduction = Math.min(10, warnings * 5);
+ currentScore -= deduction;
+ deductions.push({
+ reason: `${warnings} Project Rule Warning(s)`,
+ amount: deduction
+ });
+ }
+
+ // Deduct for untested callers (up to 15 pts)
+ if (params.untestedCallersCount > 0) {
+ const deduction = Math.min(15, params.untestedCallersCount * 3);
+ currentScore -= deduction;
+ deductions.push({
+ reason: `${params.untestedCallersCount} Affected Caller(s) without test coverage`,
+ amount: deduction
+ });
+ }
+
+ // Check failures
+ if (params.checks.tsCheck.status === 'FAIL') {
+ currentScore -= 20;
+ deductions.push({ reason: 'TypeScript compilation failure', amount: 20 });
+ }
+ if (params.checks.lintCheck.status === 'FAIL') {
+ currentScore -= 10;
+ deductions.push({ reason: 'Linter reported errors', amount: 10 });
+ }
+ if (params.checks.unitTestsCheck.status === 'FAIL') {
+ currentScore -= 25;
+ deductions.push({ reason: 'Unit test suite failed', amount: 25 });
+ }
+
+ return {
+ total: Math.max(0, Math.min(100, currentScore)),
+ deductions
+ };
+ }
+
+ private determineVerdict(
+ score: number,
+ risks: RiskFinding[],
+ rules: { status: string }[],
+ checks: {
+ tsCheck: CheckResult;
+ lintCheck: CheckResult;
+ unitTestsCheck: CheckResult;
+ }
+ ): VerificationVerdict {
+ const hasHighRisk = risks.some((r) => r.severity === 'HIGH');
+ const hasRuleViolation = rules.some((r) => r.status === 'VIOLATION');
+ const hasTestFailure = checks.unitTestsCheck.status === 'FAIL';
+ const hasTsFailure = checks.tsCheck.status === 'FAIL';
+
+ if (hasHighRisk || hasRuleViolation || hasTestFailure || hasTsFailure || score < 50) {
+ return 'BLOCKED';
+ }
+
+ if (score < 85 || risks.some((r) => r.severity === 'MEDIUM')) {
+ return 'NEEDS_REVIEW';
+ }
+
+ return 'READY_TO_SHIP';
+ }
+
+ private computeBreakageRisks(
+ impact: ImpactAnalysis,
+ risks: RiskFinding[],
+ rules: RuleResult[]
+ ): BreakageRiskItem[] {
+ const breakage: BreakageRiskItem[] = [];
+
+ // 1. Check for touched auth/session services affecting routes/callers
+ const authSymbols = impact.changedSymbols.filter(
+ (s) =>
+ /auth|session|token|user|login|permission/i.test(s.symbolName) ||
+ /auth|session/i.test(s.filePath)
+ );
+ for (const s of authSymbols) {
+ if (s.callers.length > 0) {
+ const untested = s.callers.filter((c) => !c.hasTest);
+ breakage.push({
+ id: `BRK-AUTH-${s.symbolName}`,
+ severity: 'HIGH',
+ area: 'Authentication & Session Flow',
+ trigger: `Symbol '${s.symbolName}' was modified in ${path.basename(s.filePath)}.`,
+ detail: `${s.callers.length} downstream caller(s) depend on this behavior.${
+ untested.length > 0 ? ` ${untested.length} caller(s) lack automated test coverage.` : ''
+ }`,
+ affectedCallersOrRoutes: s.callers.map((c) => `${c.callerName} (${path.basename(c.filePath)})`),
+ evidenceFile: s.filePath
+ });
+ }
+ }
+
+ // 2. Check for high risks on routes & logic (SQL injection, Missing Auth, Secrets)
+ for (const r of risks.filter((r) => r.severity === 'HIGH')) {
+ breakage.push({
+ id: `BRK-RISK-${r.id}`,
+ severity: 'HIGH',
+ area: r.category === 'authorization' ? 'API Access Control' : 'Data Security',
+ trigger: r.title,
+ detail: r.description,
+ affectedCallersOrRoutes: [path.basename(r.file)],
+ evidenceFile: r.file,
+ evidenceLine: r.line
+ });
+ }
+
+ // 3. Check for rule violations (e.g. sensitive data exposure or ownership)
+ for (const rule of rules.filter((res) => res.status === 'VIOLATION')) {
+ breakage.push({
+ id: `BRK-RULE-${rule.rule.id}`,
+ severity: rule.rule.severity,
+ area: 'Project Policy & Architecture',
+ trigger: `Violated: "${rule.rule.title}"`,
+ detail: rule.detectedMessage || rule.rule.description,
+ affectedCallersOrRoutes: rule.file ? [path.basename(rule.file)] : [],
+ evidenceFile: rule.file,
+ evidenceLine: rule.line
+ });
+ }
+
+ // 4. Untested Callers on Core Services
+ const generalUntested = impact.changedSymbols.filter(
+ (s) => !authSymbols.includes(s) && s.callers.some((c) => !c.hasTest)
+ );
+ for (const s of generalUntested.slice(0, 5)) {
+ const untested = s.callers.filter((c) => !c.hasTest);
+ breakage.push({
+ id: `BRK-UNTESTED-${s.symbolName}`,
+ severity: 'MEDIUM',
+ area: 'Regression Vulnerability',
+ trigger: `Modified '${s.symbolName}' has untested dependents.`,
+ detail: `${untested.length} caller(s) do not have regression tests to verify that this change does not break them.`,
+ affectedCallersOrRoutes: untested.map((c) => `${c.callerName} (${path.basename(c.filePath)})`),
+ evidenceFile: s.filePath
+ });
+ }
+
+ return breakage;
+ }
+}
diff --git a/src/engine/reportFormatter.ts b/src/engine/reportFormatter.ts
new file mode 100644
index 0000000..2a41985
--- /dev/null
+++ b/src/engine/reportFormatter.ts
@@ -0,0 +1,196 @@
+import { VerificationReport } from '../types';
+
+export class ReportFormatter {
+ /**
+ * Formats the verification report into clean GitHub Flavored Markdown
+ */
+ public static toMarkdown(report: VerificationReport): string {
+ const verdictBadge =
+ report.verdict === 'READY_TO_SHIP'
+ ? '🟢 **READY TO SHIP**'
+ : report.verdict === 'NEEDS_REVIEW'
+ ? '🟠 **NEEDS REVIEW**'
+ : '🔴 **BLOCKED**';
+
+ let md = `# 🛡️ ProofCode Verification Report\n\n`;
+ md += `> **AI writes code. ProofCode proves the change.**\n\n`;
+ md += `- **Verdict:** ${verdictBadge}\n`;
+ md += `- **Verification Score:** **${report.score.total}%**\n`;
+ md += `- **Branch:** \`${report.branch || 'HEAD'}\`\n`;
+ md += `- **Timestamp:** \`${report.timestamp}\`\n\n`;
+
+ md += `## 📊 Change Summary\n\n`;
+ md += `| Metric | Count |\n`;
+ md += `|---|---|\n`;
+ md += `| Files Changed | **${report.summary.filesChanged}** |\n`;
+ md += `| Lines Added | +${report.summary.linesAdded} |\n`;
+ md += `| Lines Deleted | -${report.summary.linesDeleted} |\n`;
+ md += `| Functions Affected | **${report.summary.symbolsAffected}** |\n`;
+ md += `| API Routes Affected | **${report.summary.apiRoutesAffected}** |\n`;
+ md += `| High Risks Detected | 🔴 **${report.summary.highRiskCount}** |\n`;
+ md += `| Medium Risks Detected | 🟠 **${report.summary.mediumRiskCount}** |\n`;
+ md += `| Low Risks Detected | 🟢 **${report.summary.lowRiskCount}** |\n\n`;
+
+ // Score Deductions breakdown
+ if (report.score.deductions.length > 0) {
+ md += `### Score Deductions Breakdown\n\n`;
+ for (const d of report.score.deductions) {
+ md += `- -${d.amount} pts: ${d.reason}\n`;
+ }
+ md += `\n`;
+ }
+
+ // What Could This Change Break?
+ if (report.breakageRisks && report.breakageRisks.length > 0) {
+ md += `## 💥 What Could This Change Break?\n\n`;
+ md += `| Area | Severity | Trigger & Impact | Dependents At Risk |\n`;
+ md += `|---|---|---|---|\n`;
+ for (const b of report.breakageRisks) {
+ const icon = b.severity === 'HIGH' ? '🔴' : b.severity === 'MEDIUM' ? '🟠' : '🟢';
+ const deps = b.affectedCallersOrRoutes.length > 0 ? b.affectedCallersOrRoutes.map((c) => `\`${c}\``).join(', ') : '*None*';
+ md += `| **${b.area}** | ${icon} ${b.severity} | **${b.trigger}**
${b.detail} | ${deps} |\n`;
+ }
+ md += `\n`;
+ }
+
+ // Risks & Evidence
+ md += `## ⚠️ Risks & Verifiable Evidence\n\n`;
+ if (report.risks.length === 0) {
+ md += `*No risks detected in current changes. All static checks passed.*\n\n`;
+ } else {
+ for (const risk of report.risks) {
+ const severityIcon =
+ risk.severity === 'HIGH' ? '🔴' : risk.severity === 'MEDIUM' ? '🟠' : '🟢';
+ md += `### ${severityIcon} [${risk.severity}] ${risk.title}\n\n`;
+ md += `**Location:** \`${risk.file}:${risk.line}\`\n\n`;
+ md += `**Description:** ${risk.description}\n\n`;
+ md += `**Recommendation:** ${risk.recommendation}\n\n`;
+
+ if (risk.evidenceTrace.length > 0) {
+ md += `**Evidence Path:**\n\`\`\`text\n`;
+ for (let i = 0; i < risk.evidenceTrace.length; i++) {
+ const step = risk.evidenceTrace[i];
+ const arrow = i === risk.evidenceTrace.length - 1 ? '└──' : '├──';
+ md += `${arrow} [Line ${step.line}] ${step.description}\n`;
+ }
+ md += `\`\`\`\n\n`;
+ }
+
+ if (risk.snippet) {
+ md += `**Code Snippet:**\n\`\`\`typescript\n${risk.snippet}\n\`\`\`\n\n`;
+ }
+ }
+ }
+
+ // Impact Analysis
+ md += `## ⚡ Change Impact Analysis\n\n`;
+ if (report.impact.changedSymbols.length === 0) {
+ md += `*No exported symbols were modified.*\n\n`;
+ } else {
+ md += `### Affected Symbols and Callers\n\n`;
+ for (const sym of report.impact.changedSymbols) {
+ md += `- **\`${sym.symbolName}\`** (${sym.kind}) in \`${sym.filePath}\`\n`;
+ if (sym.callers.length === 0) {
+ md += ` - *No external callers detected.*\n`;
+ } else {
+ for (const caller of sym.callers) {
+ const testBadge = caller.hasTest ? '✓ tested' : '⚠️ untested';
+ md += ` - Called by \`${caller.callerName}\` at \`${caller.filePath}:${caller.line}\` [${testBadge}]\n`;
+ }
+ }
+ }
+ md += `\n`;
+ }
+
+ if (report.impact.affectedRoutes.length > 0) {
+ md += `### Affected API Routes\n\n`;
+ for (const route of report.impact.affectedRoutes) {
+ md += `- \`${route.method}\` \`${route.routePath}\` at line ${route.line}\n`;
+ }
+ md += `\n`;
+ }
+
+ // Project Rules
+ md += `## 📋 Project Rules Status\n\n`;
+ if (report.rules.length === 0) {
+ md += `*No project rules configured.*\n\n`;
+ } else {
+ md += `| Rule | Status | Evidence |\n`;
+ md += `|---|---|---|\n`;
+ for (const r of report.rules) {
+ const statusEmoji = r.status === 'PASS' ? '✅ PASS' : r.status === 'VIOLATION' ? '🔴 VIOLATION' : '⚠️ WARNING';
+ const evidence = r.detectedMessage || 'Compliant with policy';
+ md += `| ${r.rule.title} | ${statusEmoji} | ${evidence} |\n`;
+ }
+ md += `\n`;
+ }
+
+ // Checks & Tests
+ md += `## 🧪 Checks & Verification Tests\n\n`;
+ md += `| Check | Status | Details |\n`;
+ md += `|---|---|---|\n`;
+ md += `| TypeScript | ${report.checks.typescript.status} | ${report.checks.typescript.message || ''} |\n`;
+ md += `| ESLint | ${report.checks.eslint.status} | ${report.checks.eslint.message || ''} |\n`;
+ md += `| Unit Tests | ${report.checks.unitTests.status} | ${report.checks.unitTests.message || ''} |\n`;
+ md += `| Build Check | ${report.checks.build.status} | ${report.checks.build.message || ''} |\n\n`;
+
+ md += `---\n*Generated by ProofCode v0.1.0 — Code Change Verification*\n`;
+
+ return md;
+ }
+
+ /**
+ * Formats a concise summary for terminal/CLI usage
+ */
+ public static toTerminal(report: VerificationReport): string {
+ const lines: string[] = [];
+ lines.push('====================================================');
+ lines.push(' PROOFCODE CHANGE VERIFICATION ');
+ lines.push('====================================================');
+ lines.push(`Verdict: ${report.verdict}`);
+ lines.push(`Score: ${report.score.total}%`);
+ lines.push(`Files Changed: ${report.summary.filesChanged}`);
+ lines.push(`Functions: ${report.summary.symbolsAffected} affected`);
+ lines.push(`API Routes: ${report.summary.apiRoutesAffected} affected`);
+ lines.push(
+ `Risks: 🔴 ${report.summary.highRiskCount} High | 🟠 ${report.summary.mediumRiskCount} Med | 🟢 ${report.summary.lowRiskCount} Low`
+ );
+ lines.push('----------------------------------------------------');
+
+ if (report.breakageRisks && report.breakageRisks.length > 0) {
+ lines.push('WHAT COULD THIS CHANGE BREAK?');
+ for (const b of report.breakageRisks) {
+ lines.push(`- [${b.severity}] ${b.area}: ${b.trigger}`);
+ lines.push(` Impact: ${b.detail}`);
+ if (b.affectedCallersOrRoutes.length > 0) {
+ lines.push(` Dependents: ${b.affectedCallersOrRoutes.join(', ')}`);
+ }
+ }
+ lines.push('----------------------------------------------------');
+ }
+
+ if (report.risks.length > 0) {
+ lines.push('CRITICAL FINDINGS:');
+ for (const risk of report.risks) {
+ lines.push(`[${risk.severity}] ${risk.title} at ${risk.file}:${risk.line}`);
+ lines.push(` -> ${risk.description}`);
+ }
+ lines.push('----------------------------------------------------');
+ }
+
+ const violations = report.rules.filter((r) => r.status === 'VIOLATION');
+ if (violations.length > 0) {
+ lines.push('RULE VIOLATIONS:');
+ for (const v of violations) {
+ lines.push(`- ${v.rule.title}: ${v.detectedMessage} (${v.file}:${v.line})`);
+ }
+ lines.push('----------------------------------------------------');
+ }
+
+ return lines.join('\n');
+ }
+
+ public static toJson(report: VerificationReport): string {
+ return JSON.stringify(report, null, 2);
+ }
+}
diff --git a/src/extension/index.ts b/src/extension/index.ts
new file mode 100644
index 0000000..d6d1f7a
--- /dev/null
+++ b/src/extension/index.ts
@@ -0,0 +1,306 @@
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import * as vscode from 'vscode';
+import { ProofCodeEngine } from '../engine/proofCodeEngine';
+import { ReportFormatter } from '../engine/reportFormatter';
+import { ProjectRulesEngine } from '../rules/projectRulesEngine';
+import { VerificationReport } from '../types';
+import { ProofCodeCodeActionProvider } from '../ui/codeActionProvider';
+import { ProofCodeCodeLensProvider } from '../ui/codeLensProvider';
+import { ProofCodeTreeDataProvider } from '../ui/treeDataProvider';
+import { ProofCodeWebviewPanel } from '../ui/webviewPanel';
+
+let treeDataProvider: ProofCodeTreeDataProvider;
+let statusBarItem: vscode.StatusBarItem;
+let codeLensProvider: ProofCodeCodeLensProvider;
+let diagnosticCollection: vscode.DiagnosticCollection;
+let latestReport: VerificationReport | null = null;
+
+export function activate(context: vscode.ExtensionContext): void {
+ // 1. Sidebar Tree View
+ treeDataProvider = new ProofCodeTreeDataProvider();
+ vscode.window.registerTreeDataProvider('proofcode.views.overview', treeDataProvider);
+
+ // 2. Status Bar Item
+ statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
+ statusBarItem.command = 'proofcode.openDashboard';
+ statusBarItem.text = '$(shield) ProofCode';
+ statusBarItem.tooltip = 'ProofCode Change Verification: Click to open dashboard';
+ statusBarItem.show();
+ context.subscriptions.push(statusBarItem);
+
+ // 3. Diagnostics, CodeLens & Quick Fix Code Actions
+ codeLensProvider = new ProofCodeCodeLensProvider();
+ const codeLensDisposable = vscode.languages.registerCodeLensProvider(
+ [
+ { language: 'typescript' },
+ { language: 'javascript' },
+ { language: 'typescriptreact' },
+ { language: 'javascriptreact' }
+ ],
+ codeLensProvider
+ );
+ diagnosticCollection = vscode.languages.createDiagnosticCollection('proofcode');
+
+ const codeActionProvider = new ProofCodeCodeActionProvider();
+ const codeActionDisposable = vscode.languages.registerCodeActionsProvider(
+ [
+ { language: 'typescript' },
+ { language: 'javascript' },
+ { language: 'typescriptreact' },
+ { language: 'javascriptreact' }
+ ],
+ codeActionProvider,
+ {
+ providedCodeActionKinds: ProofCodeCodeActionProvider.providedCodeActionKinds
+ }
+ );
+
+ context.subscriptions.push(codeLensDisposable, diagnosticCollection, codeActionDisposable);
+
+ // Helper to get workspace root
+ const getWorkspaceRoot = (): string | undefined => {
+ return vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0
+ ? vscode.workspace.workspaceFolders[0].uri.fsPath
+ : undefined;
+ };
+
+ // Verification Runner
+ const runVerification = async (quiet = false): Promise => {
+ const root = getWorkspaceRoot();
+ if (!root) {
+ if (!quiet) {
+ vscode.window.showWarningMessage('ProofCode requires an open workspace folder.');
+ }
+ return null;
+ }
+
+ const config = vscode.workspace.getConfiguration('proofcode');
+ const runLiveTests = config.get('runTestsOnVerify', false);
+ const rulesFilePath = config.get('rulesFilePath', '.proofcode/rules.md');
+
+ return vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Notification,
+ title: 'ProofCode: Verifying change impact and risks...',
+ cancellable: false
+ },
+ async () => {
+ try {
+ const engine = new ProofCodeEngine(root);
+ const report = await engine.verify({
+ runLiveTests,
+ rulesFilePath
+ });
+
+ latestReport = report;
+ treeDataProvider.setReport(report);
+
+ // Update Diagnostics in Problems panel
+ diagnosticCollection.clear();
+ const fileDiagnosticsMap = new Map();
+
+ for (const risk of report.risks) {
+ const lineIdx = Math.max(0, risk.line - 1);
+ const colIdx = Math.max(0, (risk.column || 1) - 1);
+ const range = new vscode.Range(lineIdx, colIdx, lineIdx, colIdx + 40);
+ const severity =
+ risk.severity === 'HIGH'
+ ? vscode.DiagnosticSeverity.Error
+ : risk.severity === 'MEDIUM'
+ ? vscode.DiagnosticSeverity.Warning
+ : vscode.DiagnosticSeverity.Information;
+
+ const diag = new vscode.Diagnostic(
+ range,
+ `[${risk.ruleId}] ${risk.title}: ${risk.description}`,
+ severity
+ );
+ diag.source = 'ProofCode';
+ diag.code = risk.ruleId;
+
+ const list = fileDiagnosticsMap.get(risk.file) || [];
+ list.push(diag);
+ fileDiagnosticsMap.set(risk.file, list);
+ }
+
+ for (const rule of report.rules) {
+ if (rule.status === 'VIOLATION' && rule.file) {
+ const lineIdx = Math.max(0, (rule.line || 1) - 1);
+ const range = new vscode.Range(lineIdx, 0, lineIdx, 40);
+ const diag = new vscode.Diagnostic(
+ range,
+ `[${rule.rule.id}] Rule Violation: "${rule.rule.title}" - ${rule.detectedMessage || ''}`,
+ vscode.DiagnosticSeverity.Error
+ );
+ diag.source = 'ProofCode';
+ diag.code = rule.rule.id;
+
+ const list = fileDiagnosticsMap.get(rule.file) || [];
+ list.push(diag);
+ fileDiagnosticsMap.set(rule.file, list);
+ }
+ }
+
+ for (const [filePath, diags] of fileDiagnosticsMap.entries()) {
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(root, filePath);
+ diagnosticCollection.set(vscode.Uri.file(fullPath), diags);
+ }
+
+ // Update CodeLens annotations
+ codeLensProvider.setReport(report);
+
+ // Update Status Bar
+ const score = report.score.total;
+ const risksCount = report.risks.length;
+ const icon =
+ report.verdict === 'READY_TO_SHIP'
+ ? '$(pass)'
+ : report.verdict === 'NEEDS_REVIEW'
+ ? '$(warning)'
+ : '$(error)';
+
+ statusBarItem.text = `${icon} ProofCode: ${score}% (${risksCount} risks)`;
+ statusBarItem.tooltip = `Verdict: ${report.verdict} | ${report.summary.filesChanged} files changed, ${report.summary.symbolsAffected} symbols affected.`;
+
+ // If webview is open, refresh it
+ if (ProofCodeWebviewPanel.currentPanel) {
+ ProofCodeWebviewPanel.currentPanel.setReport(report);
+ }
+
+ if (!quiet) {
+ if (report.verdict === 'READY_TO_SHIP') {
+ vscode.window.showInformationMessage(
+ `ProofCode: Verification Passed (${score}%). Ready to ship!`
+ );
+ } else if (report.verdict === 'NEEDS_REVIEW') {
+ vscode.window.showWarningMessage(
+ `ProofCode: Score ${score}%. ${report.summary.mediumRiskCount} warning(s) need review.`
+ );
+ } else {
+ vscode.window.showErrorMessage(
+ `ProofCode: BLOCKED (${score}%). ${report.summary.highRiskCount} high risk(s) or rule violations detected.`
+ );
+ }
+ }
+
+ return report;
+ } catch (err: unknown) {
+ vscode.window.showErrorMessage(
+ `ProofCode Verification Error: ${(err as Error).message}`
+ );
+ return null;
+ }
+ }
+ );
+ };
+
+ // Commands
+ const verifyCmd = vscode.commands.registerCommand('proofcode.verifyChange', async () => {
+ await runVerification(false);
+ });
+
+ const openDashboardCmd = vscode.commands.registerCommand('proofcode.openDashboard', async () => {
+ if (!latestReport) {
+ await runVerification(true);
+ }
+
+ const root = getWorkspaceRoot();
+ ProofCodeWebviewPanel.createOrShow(
+ context.extensionUri,
+ latestReport,
+ async () => {
+ await runVerification(false);
+ },
+ async () => {
+ if (root) {
+ const rulesEngine = new ProjectRulesEngine(root);
+ const createdPath = await rulesEngine.initDefaultRulesFile();
+ const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(createdPath));
+ await vscode.window.showTextDocument(doc);
+ }
+ }
+ );
+ });
+
+ const initRulesCmd = vscode.commands.registerCommand('proofcode.initRules', async () => {
+ const root = getWorkspaceRoot();
+ if (!root) return;
+
+ const rulesEngine = new ProjectRulesEngine(root);
+ const createdPath = await rulesEngine.initDefaultRulesFile();
+ const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(createdPath));
+ await vscode.window.showTextDocument(doc);
+ vscode.window.showInformationMessage(`ProofCode: Initialized ${createdPath}`);
+ });
+
+ const openEvidenceCmd = vscode.commands.registerCommand(
+ 'proofcode.openEvidence',
+ async (filePath: string, line: number) => {
+ try {
+ const root = getWorkspaceRoot() || '';
+ const targetPath = path.isAbsolute(filePath) ? filePath : path.resolve(root, filePath);
+ const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(targetPath));
+ const editor = await vscode.window.showTextDocument(doc);
+ const position = new vscode.Position(Math.max(0, line - 1), 0);
+ editor.selection = new vscode.Selection(position, position);
+ editor.revealRange(
+ new vscode.Range(position, position),
+ vscode.TextEditorRevealType.InCenter
+ );
+ } catch (err: unknown) {
+ vscode.window.showErrorMessage(`Could not open file: ${(err as Error).message}`);
+ }
+ }
+ );
+
+ const exportReportCmd = vscode.commands.registerCommand('proofcode.exportReport', async () => {
+ const root = getWorkspaceRoot();
+ if (!root || !latestReport) {
+ vscode.window.showWarningMessage('No report available to export. Run verification first.');
+ return;
+ }
+
+ const md = ReportFormatter.toMarkdown(latestReport);
+ const exportPath = path.join(root, '.proofcode', 'report.md');
+ const dir = path.dirname(exportPath);
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ }
+
+ fs.writeFileSync(exportPath, md, 'utf8');
+ const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(exportPath));
+ await vscode.window.showTextDocument(doc);
+ vscode.window.showInformationMessage(`ProofCode: Verification report exported to ${exportPath}`);
+ });
+
+ // Auto-verify on save if configured
+ const onSaveDisposable = vscode.workspace.onDidSaveTextDocument(async (doc) => {
+ if (doc.fileName.includes('.git') || doc.fileName.includes('node_modules')) return;
+ const config = vscode.workspace.getConfiguration('proofcode');
+ if (config.get('autoVerifyOnSave', true)) {
+ await runVerification(true);
+ }
+ });
+
+ context.subscriptions.push(
+ verifyCmd,
+ openDashboardCmd,
+ initRulesCmd,
+ openEvidenceCmd,
+ exportReportCmd,
+ onSaveDisposable
+ );
+
+ // Initial silent run
+ runVerification(true).catch(() => {});
+}
+
+export function deactivate(): void {
+ if (statusBarItem) {
+ statusBarItem.dispose();
+ }
+ if (diagnosticCollection) {
+ diagnosticCollection.dispose();
+ }
+}
diff --git a/src/git/diffParser.ts b/src/git/diffParser.ts
new file mode 100644
index 0000000..028f0ef
--- /dev/null
+++ b/src/git/diffParser.ts
@@ -0,0 +1,155 @@
+import { ChangedLineRange, DiffHunk, FileDiff, GitDiffResult } from '../types';
+
+/**
+ * Parses unified git diff output into structured FileDiff models
+ */
+export class DiffParser {
+ public static parse(rawDiff: string, repoRoot: string = ''): GitDiffResult {
+ const files: FileDiff[] = [];
+ if (!rawDiff || rawDiff.trim().length === 0) {
+ return {
+ repoRoot,
+ files: [],
+ totalFilesChanged: 0,
+ totalInsertions: 0,
+ totalDeletions: 0
+ };
+ }
+
+ const lines = rawDiff.split('\n');
+ let currentFile: FileDiff | null = null;
+ let currentHunk: DiffHunk | null = null;
+ let currentNewLine = 0;
+ let totalInsertions = 0;
+ let totalDeletions = 0;
+
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+
+ // New file diff header: diff --git a/path b/path
+ if (line.startsWith('diff --git ')) {
+ if (currentHunk && currentFile) {
+ currentFile.hunks.push(currentHunk);
+ currentHunk = null;
+ }
+ if (currentFile) {
+ files.push(currentFile);
+ }
+
+ const match = line.match(/^diff --git a\/(.+) b\/(.+)$/);
+ const oldPath = match ? match[1] : '';
+ const newPath = match ? match[2] : '';
+
+ currentFile = {
+ oldPath,
+ newPath: newPath || oldPath,
+ isNew: false,
+ isDeleted: false,
+ isRenamed: false,
+ hunks: [],
+ addedLines: [],
+ deletedLines: [],
+ modifiedLineRanges: []
+ };
+ continue;
+ }
+
+ if (!currentFile) {
+ continue;
+ }
+
+ if (line.startsWith('new file mode ')) {
+ currentFile.isNew = true;
+ } else if (line.startsWith('deleted file mode ')) {
+ currentFile.isDeleted = true;
+ } else if (line.startsWith('similarity index ')) {
+ currentFile.isRenamed = true;
+ } else if (line.startsWith('rename from ')) {
+ currentFile.oldPath = line.substring('rename from '.length).trim();
+ } else if (line.startsWith('rename to ')) {
+ currentFile.newPath = line.substring('rename to '.length).trim();
+ } else if (line.startsWith('@@ ')) {
+ // Hunk header: @@ -oldStart,oldLines +newStart,newLines @@ [optional context]
+ if (currentHunk) {
+ currentFile.hunks.push(currentHunk);
+ }
+
+ const hunkMatch = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/);
+ if (hunkMatch) {
+ const oldStart = parseInt(hunkMatch[1], 10);
+ const oldLines = hunkMatch[2] ? parseInt(hunkMatch[2], 10) : 1;
+ const newStart = parseInt(hunkMatch[3], 10);
+ const newLines = hunkMatch[4] ? parseInt(hunkMatch[4], 10) : 1;
+
+ currentHunk = {
+ oldStart,
+ oldLines,
+ newStart,
+ newLines,
+ header: line,
+ lines: []
+ };
+ currentNewLine = newStart;
+ }
+ } else if (currentHunk) {
+ currentHunk.lines.push(line);
+
+ if (line.startsWith('+') && !line.startsWith('+++')) {
+ currentFile.addedLines.push(currentNewLine);
+ totalInsertions++;
+ currentNewLine++;
+ } else if (line.startsWith('-') && !line.startsWith('---')) {
+ currentFile.deletedLines.push(currentHunk.newStart);
+ totalDeletions++;
+ } else if (line.startsWith(' ')) {
+ currentNewLine++;
+ }
+ }
+ }
+
+ if (currentHunk && currentFile) {
+ currentFile.hunks.push(currentHunk);
+ }
+ if (currentFile) {
+ files.push(currentFile);
+ }
+
+ // Compute consolidated modifiedLineRanges per file
+ for (const file of files) {
+ file.modifiedLineRanges = this.computeLineRanges(file.addedLines);
+ }
+
+ return {
+ repoRoot,
+ files,
+ totalFilesChanged: files.length,
+ totalInsertions,
+ totalDeletions
+ };
+ }
+
+ private static computeLineRanges(lineNumbers: number[]): ChangedLineRange[] {
+ if (lineNumbers.length === 0) {
+ return [];
+ }
+
+ const sorted = Array.from(new Set(lineNumbers)).sort((a, b) => a - b);
+ const ranges: ChangedLineRange[] = [];
+ let start = sorted[0];
+ let end = sorted[0];
+
+ for (let i = 1; i < sorted.length; i++) {
+ const current = sorted[i];
+ if (current === end + 1) {
+ end = current;
+ } else {
+ ranges.push({ start, end });
+ start = current;
+ end = current;
+ }
+ }
+ ranges.push({ start, end });
+
+ return ranges;
+ }
+}
diff --git a/src/git/gitService.ts b/src/git/gitService.ts
new file mode 100644
index 0000000..f7bbdc3
--- /dev/null
+++ b/src/git/gitService.ts
@@ -0,0 +1,175 @@
+import { exec } from 'node:child_process';
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import { promisify } from 'node:util';
+import { DiffParser } from './diffParser';
+import { GitDiffResult, GitFileStatus } from '../types';
+
+const execAsync = promisify(exec);
+
+export interface GitServiceOptions {
+ cwd?: string;
+ maxBuffer?: number;
+}
+
+export class GitService {
+ private cwd: string;
+ private maxBuffer: number;
+
+ constructor(options: GitServiceOptions = {}) {
+ this.cwd = options.cwd || process.cwd();
+ this.maxBuffer = options.maxBuffer || 10 * 1024 * 1024; // 10MB
+ }
+
+ public async getRepoRoot(): Promise {
+ try {
+ const { stdout } = await execAsync('git rev-parse --show-toplevel', {
+ cwd: this.cwd,
+ maxBuffer: this.maxBuffer
+ });
+ return stdout.trim();
+ } catch {
+ return this.cwd;
+ }
+ }
+
+ public async getCurrentBranch(repoRoot?: string): Promise {
+ const cwd = repoRoot || this.cwd;
+ try {
+ const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD', {
+ cwd,
+ maxBuffer: this.maxBuffer
+ });
+ return stdout.trim();
+ } catch {
+ return 'HEAD';
+ }
+ }
+
+ public async getStatus(repoRoot?: string): Promise {
+ const cwd = repoRoot || this.cwd;
+ try {
+ const { stdout } = await execAsync('git status --porcelain -uall', {
+ cwd,
+ maxBuffer: this.maxBuffer
+ });
+
+ const results: GitFileStatus[] = [];
+ const lines = stdout.split('\n');
+
+ for (const rawLine of lines) {
+ if (!rawLine || rawLine.trim().length === 0) continue;
+ const indexStatus = rawLine[0];
+ const workTreeStatus = rawLine[1];
+ const filePath = rawLine.substring(3).trim();
+
+ let status: GitFileStatus['status'] = 'modified';
+ if (indexStatus === '?' || workTreeStatus === '?') {
+ status = 'untracked';
+ } else if (indexStatus === 'A' || workTreeStatus === 'A') {
+ status = 'added';
+ } else if (indexStatus === 'D' || workTreeStatus === 'D') {
+ status = 'deleted';
+ } else if (indexStatus === 'R' || workTreeStatus === 'R') {
+ status = 'renamed';
+ }
+
+ results.push({ path: filePath, status });
+ }
+
+ return results;
+ } catch {
+ return [];
+ }
+ }
+
+ public async getDiff(options?: {
+ staged?: boolean;
+ baseRef?: string;
+ repoRoot?: string;
+ }): Promise {
+ const repoRoot = options?.repoRoot || (await this.getRepoRoot());
+ const parts: string[] = ['git diff'];
+
+ if (options?.staged) {
+ parts.push('--cached');
+ } else if (options?.baseRef) {
+ parts.push(options.baseRef);
+ } else {
+ // By default compare against HEAD to include both staged and unstaged tracked changes
+ // Check if repository has any commits yet
+ try {
+ await execAsync('git rev-parse HEAD', { cwd: repoRoot });
+ parts.push('HEAD');
+ } catch {
+ // Empty repository, diff against empty tree
+ parts.push('4b825dc642cb6eb9a060e54bf8d69288fbee4904');
+ }
+ }
+
+ let diffOutput = '';
+ try {
+ const { stdout } = await execAsync(parts.join(' '), {
+ cwd: repoRoot,
+ maxBuffer: this.maxBuffer
+ });
+ diffOutput = stdout;
+ } catch (err: unknown) {
+ // Fallback to simple git diff if HEAD ref fails
+ try {
+ const { stdout } = await execAsync('git diff', {
+ cwd: repoRoot,
+ maxBuffer: this.maxBuffer
+ });
+ diffOutput = stdout;
+ } catch {
+ diffOutput = '';
+ }
+ }
+
+ const parsed = DiffParser.parse(diffOutput, repoRoot);
+
+ // Also check for untracked files and synthesize file diffs so new AI-created files are fully verified!
+ const statuses = await this.getStatus(repoRoot);
+ const untracked = statuses.filter((s) => s.status === 'untracked');
+
+ for (const item of untracked) {
+ const fullPath = path.resolve(repoRoot, item.path);
+ try {
+ const stat = await fs.promises.stat(fullPath);
+ if (stat.isFile()) {
+ const content = await fs.promises.readFile(fullPath, 'utf8');
+ const fileLines = content.split('\n');
+ const addedLines = fileLines.map((_, idx) => idx + 1);
+
+ parsed.files.push({
+ oldPath: item.path,
+ newPath: item.path,
+ isNew: true,
+ isDeleted: false,
+ isRenamed: false,
+ hunks: [
+ {
+ oldStart: 0,
+ oldLines: 0,
+ newStart: 1,
+ newLines: fileLines.length,
+ header: `@@ -0,0 +1,${fileLines.length} @@`,
+ lines: fileLines.map((l) => `+${l}`)
+ }
+ ],
+ addedLines,
+ deletedLines: [],
+ modifiedLineRanges: [{ start: 1, end: fileLines.length }]
+ });
+ parsed.totalFilesChanged++;
+ parsed.totalInsertions += fileLines.length;
+ }
+ } catch {
+ // Skip unreadable or directory items
+ }
+ }
+
+ return parsed;
+ }
+}
diff --git a/src/graph/projectGraph.ts b/src/graph/projectGraph.ts
new file mode 100644
index 0000000..77c5801
--- /dev/null
+++ b/src/graph/projectGraph.ts
@@ -0,0 +1,373 @@
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import { AstAnalyzer } from '../analyzer/astAnalyzer';
+import {
+ AffectedCaller,
+ AffectedRoute,
+ AffectedSymbol,
+ FileAnalysis,
+ ImpactAnalysis,
+ SymbolInfo
+} from '../types';
+
+export class ProjectGraph {
+ private workspaceRoot: string;
+ private fileAnalyses = new Map();
+ private fileDependents = new Map>(); // targetFile -> Set of files that import it
+ private fileDependencies = new Map>(); // sourceFile -> Set of files it imports
+ private pathAliases = new Map(); // alias prefix -> target dir
+
+ constructor(workspaceRoot: string) {
+ this.workspaceRoot = workspaceRoot;
+ this.loadTsConfigAliases();
+ }
+
+ private loadTsConfigAliases(): void {
+ const tsconfigPath = path.join(this.workspaceRoot, 'tsconfig.json');
+ if (!fs.existsSync(tsconfigPath)) return;
+
+ try {
+ const raw = fs.readFileSync(tsconfigPath, 'utf8');
+ // Strip comments
+ const cleaned = raw.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
+ const parsed = JSON.parse(cleaned);
+ const paths = parsed.compilerOptions?.paths;
+ const baseUrl = parsed.compilerOptions?.baseUrl || '.';
+
+ if (paths && typeof paths === 'object') {
+ for (const [aliasPattern, targetList] of Object.entries(paths)) {
+ if (Array.isArray(targetList) && targetList.length > 0) {
+ const cleanAlias = aliasPattern.replace(/\*$/, '');
+ const cleanTarget = (targetList[0] as string).replace(/\*$/, '');
+ const resolvedTarget = path.resolve(this.workspaceRoot, baseUrl, cleanTarget);
+ this.pathAliases.set(cleanAlias, resolvedTarget);
+ }
+ }
+ }
+ } catch {
+ // Ignore tsconfig parsing errors
+ }
+ }
+
+ /**
+ * Scans all code files in the workspace and builds the dependency graph.
+ */
+ public async buildGraph(filterFiles?: string[]): Promise {
+ const filesToScan = filterFiles ?? this.findSourceFiles(this.workspaceRoot);
+
+ for (const file of filesToScan) {
+ const analysis = AstAnalyzer.analyzeFile(file);
+ this.fileAnalyses.set(file, analysis);
+ }
+
+ // Resolve dependencies and construct reverse dependents graph
+ for (const [sourceFile, analysis] of this.fileAnalyses.entries()) {
+ const deps = new Set();
+
+ for (const imp of analysis.imports) {
+ const resolved = this.resolveImportPath(sourceFile, imp.source);
+ if (resolved) {
+ deps.add(resolved);
+ imp.resolvedPath = resolved;
+
+ let reverseSet = this.fileDependents.get(resolved);
+ if (!reverseSet) {
+ reverseSet = new Set();
+ this.fileDependents.set(resolved, reverseSet);
+ }
+ reverseSet.add(sourceFile);
+ }
+ }
+
+ this.fileDependencies.set(sourceFile, deps);
+ }
+ }
+
+ public resolveImportPath(currentFile: string, importSource: string): string | null {
+ // 1. Relative import
+ if (importSource.startsWith('.')) {
+ const dir = path.dirname(currentFile);
+ const targetCandidate = path.resolve(dir, importSource);
+ return this.tryResolveFileExtensions(targetCandidate);
+ }
+
+ // 2. Check path aliases (e.g. "@/..." or "~/...")
+ for (const [alias, targetBase] of this.pathAliases.entries()) {
+ if (importSource.startsWith(alias)) {
+ const remainder = importSource.substring(alias.length);
+ const resolvedCandidate = path.resolve(targetBase, remainder);
+ return this.tryResolveFileExtensions(resolvedCandidate);
+ }
+ }
+
+ return null;
+ }
+
+ private tryResolveFileExtensions(basePath: string): string | null {
+ const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
+
+ // Direct file match
+ if (fs.existsSync(basePath) && fs.statSync(basePath).isFile()) {
+ return basePath;
+ }
+
+ // Match with extension
+ for (const ext of extensions) {
+ const candidate = `${basePath}${ext}`;
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
+ return candidate;
+ }
+ }
+
+ // Match directory index file
+ if (fs.existsSync(basePath) && fs.statSync(basePath).isDirectory()) {
+ for (const ext of extensions) {
+ const indexCandidate = path.join(basePath, `index${ext}`);
+ if (fs.existsSync(indexCandidate) && fs.statSync(indexCandidate).isFile()) {
+ return indexCandidate;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public findSourceFiles(dir: string, fileList: string[] = []): string[] {
+ if (!fs.existsSync(dir)) return fileList;
+
+ const ignoredDirs = new Set([
+ 'node_modules',
+ '.git',
+ 'dist',
+ 'out',
+ '.next',
+ '.turbo',
+ 'coverage',
+ 'build'
+ ]);
+
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ for (const entry of entries) {
+ const fullPath = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ if (!ignoredDirs.has(entry.name) && !entry.name.startsWith('.')) {
+ this.findSourceFiles(fullPath, fileList);
+ }
+ } else if (entry.isFile()) {
+ if (/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
+ fileList.push(fullPath);
+ }
+ }
+ }
+
+ return fileList;
+ }
+
+ public getAnalysis(filePath: string): FileAnalysis | undefined {
+ return this.fileAnalyses.get(filePath);
+ }
+
+ public getDependents(filePath: string): Set {
+ return this.fileDependents.get(filePath) || new Set();
+ }
+
+ public isTestFile(filePath: string): boolean {
+ const normalized = filePath.replace(/\\/g, '/');
+ return (
+ normalized.includes('.test.') ||
+ normalized.includes('.spec.') ||
+ normalized.includes('/__tests__/') ||
+ normalized.includes('/tests/')
+ );
+ }
+
+ /**
+ * Computes the change impact across the codebase given a map of changed files and lines.
+ */
+ public computeImpact(fileChangedLines: Map): ImpactAnalysis {
+ const changedFiles = Array.from(fileChangedLines.keys());
+ const affectedFilesSet = new Set();
+ const affectedCallers: AffectedCaller[] = [];
+ const affectedRoutes: AffectedRoute[] = [];
+ const changedSymbolsList: AffectedSymbol[] = [];
+
+ // Collect all test files in the workspace for test coverage check
+ const allTestFiles = Array.from(this.fileAnalyses.keys()).filter((f) => this.isTestFile(f));
+
+ for (const [changedFile, lineNumbers] of fileChangedLines.entries()) {
+ const analysis = this.fileAnalyses.get(changedFile);
+ if (!analysis) continue;
+
+ // Find changed symbols in this file
+ const touchedSymbols = AstAnalyzer.findSymbolsAtLines(analysis.symbols, lineNumbers);
+
+ for (const sym of touchedSymbols) {
+ const callers = this.findCallersForSymbol(sym, changedFile, allTestFiles);
+ const isApiRoute =
+ sym.kind === 'api_route' ||
+ (analysis.hasApiRoute &&
+ /^(GET|POST|PUT|DELETE|PATCH)$/i.test(sym.name));
+
+ changedSymbolsList.push({
+ symbolName: sym.name,
+ kind: sym.kind,
+ filePath: changedFile,
+ callers,
+ isApiRoute
+ });
+
+ for (const caller of callers) {
+ affectedCallers.push(caller);
+ affectedFilesSet.add(caller.filePath);
+ }
+ }
+
+ // Add direct and transitive dependents
+ const dependents = this.getTransitiveDependents(changedFile);
+ for (const dep of dependents) {
+ affectedFilesSet.add(dep);
+ const depAnalysis = this.fileAnalyses.get(dep);
+ if (depAnalysis && depAnalysis.hasApiRoute) {
+ for (const route of depAnalysis.apiRoutes) {
+ affectedRoutes.push({
+ routePath: route.path,
+ method: route.method,
+ filePath: dep,
+ line: route.line
+ });
+ }
+ }
+ }
+
+ // Check if the changed file itself has API routes
+ if (analysis.hasApiRoute) {
+ for (const route of analysis.apiRoutes) {
+ affectedRoutes.push({
+ routePath: route.path,
+ method: route.method,
+ filePath: changedFile,
+ line: route.line
+ });
+ }
+ }
+ }
+
+ // Deduplicate routes
+ const uniqueRoutesMap = new Map();
+ for (const r of affectedRoutes) {
+ const key = `${r.method}:${r.filePath}:${r.line}`;
+ uniqueRoutesMap.set(key, r);
+ }
+
+ const totalCallersCount = affectedCallers.length;
+ const untestedCallersCount = affectedCallers.filter((c) => !c.hasTest).length;
+
+ return {
+ changedFiles,
+ changedSymbols: changedSymbolsList,
+ affectedFiles: Array.from(affectedFilesSet),
+ affectedCallers,
+ affectedRoutes: Array.from(uniqueRoutesMap.values()),
+ totalCallersCount,
+ untestedCallersCount
+ };
+ }
+
+ private findCallersForSymbol(
+ sym: SymbolInfo,
+ defFile: string,
+ allTestFiles: string[]
+ ): AffectedCaller[] {
+ const callers: AffectedCaller[] = [];
+ const directDependents = this.getDependents(defFile);
+
+ // Also include same file callers
+ const defAnalysis = this.fileAnalyses.get(defFile);
+ if (defAnalysis) {
+ for (const otherSym of defAnalysis.symbols) {
+ if (otherSym.name !== sym.name && otherSym.calls.includes(sym.name)) {
+ const hasTest = this.hasTestCoverage(defFile, otherSym.name, allTestFiles);
+ callers.push({
+ callerName: otherSym.name,
+ filePath: defFile,
+ line: otherSym.startLine,
+ hasTest,
+ testFilePath: hasTest ? this.findMatchingTestFile(defFile, allTestFiles) : undefined
+ });
+ }
+ }
+ }
+
+ for (const depFile of directDependents) {
+ const depAnalysis = this.fileAnalyses.get(depFile);
+ if (!depAnalysis) continue;
+
+ // Check if depFile imports sym
+ const importsSymbol = depAnalysis.imports.some(
+ (imp) =>
+ imp.resolvedPath === defFile &&
+ (imp.specifiers.some((s) => s.name === sym.name || s.propertyName === sym.name) ||
+ imp.specifiers.some((s) => s.isNamespace || s.isDefault))
+ );
+
+ if (!importsSymbol) continue;
+
+ // Check which symbols in depFile call sym.name
+ for (const callerSym of depAnalysis.symbols) {
+ if (
+ callerSym.calls.includes(sym.name) ||
+ callerSym.calls.some((c) => c.endsWith(`.${sym.name}`))
+ ) {
+ const hasTest = this.isTestFile(depFile) || this.hasTestCoverage(depFile, callerSym.name, allTestFiles);
+ callers.push({
+ callerName: callerSym.name,
+ filePath: depFile,
+ line: callerSym.startLine,
+ hasTest,
+ testFilePath: hasTest ? (this.isTestFile(depFile) ? depFile : this.findMatchingTestFile(depFile, allTestFiles)) : undefined
+ });
+ }
+ }
+ }
+
+ return callers;
+ }
+
+ private getTransitiveDependents(file: string, visited = new Set()): Set {
+ if (visited.has(file)) return new Set();
+ visited.add(file);
+
+ const result = new Set();
+ const direct = this.getDependents(file);
+
+ for (const d of direct) {
+ result.add(d);
+ const sub = this.getTransitiveDependents(d, visited);
+ for (const s of sub) {
+ result.add(s);
+ }
+ }
+
+ return result;
+ }
+
+ private hasTestCoverage(sourceFile: string, _symbolName: string, allTestFiles: string[]): boolean {
+ const base = path.basename(sourceFile, path.extname(sourceFile));
+ for (const testFile of allTestFiles) {
+ if (testFile.includes(base)) {
+ return true;
+ }
+ // Check if testFile imports sourceFile
+ const deps = this.fileDependencies.get(testFile);
+ if (deps && deps.has(sourceFile)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private findMatchingTestFile(sourceFile: string, allTestFiles: string[]): string | undefined {
+ const base = path.basename(sourceFile, path.extname(sourceFile));
+ return allTestFiles.find((t) => t.includes(base));
+ }
+}
diff --git a/src/rules/projectRulesEngine.ts b/src/rules/projectRulesEngine.ts
new file mode 100644
index 0000000..f43723c
--- /dev/null
+++ b/src/rules/projectRulesEngine.ts
@@ -0,0 +1,312 @@
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import { ProjectGraph } from '../graph/projectGraph';
+import { FileDiff, ProjectRule, RuleResult } from '../types';
+
+export class ProjectRulesEngine {
+ private workspaceRoot: string;
+ private rulesFilePath: string;
+
+ constructor(workspaceRoot: string, customRulesPath?: string) {
+ this.workspaceRoot = workspaceRoot;
+ this.rulesFilePath = path.resolve(
+ workspaceRoot,
+ customRulesPath || '.proofcode/rules.md'
+ );
+ }
+
+ public getRulesPath(): string {
+ return this.rulesFilePath;
+ }
+
+ public hasRulesFile(): boolean {
+ return fs.existsSync(this.rulesFilePath);
+ }
+
+ /**
+ * Initializes default .proofcode/rules.md if it doesn't exist
+ */
+ public async initDefaultRulesFile(): Promise {
+ const dir = path.dirname(this.rulesFilePath);
+ if (!fs.existsSync(dir)) {
+ await fs.promises.mkdir(dir, { recursive: true });
+ }
+
+ const defaultContent = `# Project Rules
+
+Define organizational and architectural standards for your codebase. ProofCode evaluates every code change against these rules.
+
+- **Rule 1 (Authentication):** Every API route must authenticate the user.
+- **Rule 2 (Data Privacy):** Never expose passwordHash or secrets in responses.
+- **Rule 3 (Resource Ownership):** Users can only access their own orders or user resources.
+- **Rule 4 (Test Coverage):** All public API endpoints require tests.
+- **Rule 5 (Financial Safety):** Payment operations must be idempotent.
+`;
+
+ await fs.promises.writeFile(this.rulesFilePath, defaultContent, 'utf8');
+ return this.rulesFilePath;
+ }
+
+ /**
+ * Parses rules from .proofcode/rules.md
+ */
+ public parseRules(): ProjectRule[] {
+ if (!this.hasRulesFile()) {
+ return this.getDefaultRules();
+ }
+
+ try {
+ const content = fs.readFileSync(this.rulesFilePath, 'utf8');
+ const lines = content.split('\n');
+ const rules: ProjectRule[] = [];
+ let ruleIndex = 1;
+
+ for (const line of lines) {
+ const trimmed = line.trim();
+ // Match bullet items like "- Every API route..." or "- **Rule 1:** ..."
+ if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
+ const ruleText = trimmed.replace(/^[-*]\s+/, '').trim();
+ // Remove leading bold tags if present: **Rule Name:**
+ const titleMatch = ruleText.match(/^\*\*(.*?)\*\*:?\s*(.*)$/);
+ let title = ruleText;
+ let description = ruleText;
+
+ if (titleMatch) {
+ title = titleMatch[1].trim();
+ description = titleMatch[2].trim() || title;
+ }
+
+ rules.push({
+ id: `RULE-${ruleIndex++}`,
+ title: title || description,
+ description,
+ severity: 'HIGH'
+ });
+ }
+ }
+
+ return rules.length > 0 ? rules : this.getDefaultRules();
+ } catch {
+ return this.getDefaultRules();
+ }
+ }
+
+ public getDefaultRules(): ProjectRule[] {
+ return [
+ {
+ id: 'RULE-1',
+ title: 'API Authentication',
+ description: 'Every API route must authenticate the user.',
+ severity: 'HIGH'
+ },
+ {
+ id: 'RULE-2',
+ title: 'Sensitive Field Protection',
+ description: 'Never expose passwordHash or secret fields in responses.',
+ severity: 'HIGH'
+ },
+ {
+ id: 'RULE-3',
+ title: 'Resource Ownership',
+ description: 'Users can only access their own orders or sensitive resources.',
+ severity: 'HIGH'
+ },
+ {
+ id: 'RULE-4',
+ title: 'API Test Requirement',
+ description: 'All public API endpoints require tests.',
+ severity: 'MEDIUM'
+ },
+ {
+ id: 'RULE-5',
+ title: 'Payment Idempotency',
+ description: 'Payment operations must be idempotent.',
+ severity: 'MEDIUM'
+ }
+ ];
+ }
+
+ /**
+ * Checks changes against active project rules
+ */
+ public async evaluateRules(
+ fileDiffs: FileDiff[],
+ graph: ProjectGraph
+ ): Promise {
+ const rules = this.parseRules();
+ const results: RuleResult[] = [];
+
+ for (const rule of rules) {
+ const result = await this.evaluateSingleRule(rule, fileDiffs, graph);
+ results.push(result);
+ }
+
+ return results;
+ }
+
+ private async evaluateSingleRule(
+ rule: ProjectRule,
+ fileDiffs: FileDiff[],
+ graph: ProjectGraph
+ ): Promise {
+ const textLower = `${rule.title} ${rule.description}`.toLowerCase();
+
+ const resolveFile = (relPath: string): string | null => {
+ if (path.isAbsolute(relPath) && fs.existsSync(relPath)) return relPath;
+ const direct = path.resolve(this.workspaceRoot, relPath);
+ if (fs.existsSync(direct)) return direct;
+ const cwdPath = path.resolve(relPath);
+ if (fs.existsSync(cwdPath)) return cwdPath;
+ return null;
+ };
+
+ // 1. PasswordHash protection
+ if (textLower.includes('passwordhash') || textLower.includes('never expose password')) {
+ for (const diff of fileDiffs) {
+ if (!/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(diff.newPath)) continue;
+ const fullPath = resolveFile(diff.newPath);
+ if (!fullPath) continue;
+ const content = fs.readFileSync(fullPath, 'utf8');
+ const lines = content.split('\n');
+ const hasResponse = /res\.json|response\.json|return\s+\{/i.test(content);
+
+ for (const lineNum of diff.addedLines) {
+ const lineStr = lines[lineNum - 1] || '';
+ if (
+ /passwordHash|password_hash/i.test(lineStr) &&
+ (hasResponse || /res\.json|return|response\.json/i.test(lineStr))
+ ) {
+ return {
+ rule,
+ status: 'VIOLATION',
+ detectedMessage: 'Detected sensitive field "passwordHash" in response payload.',
+ file: diff.newPath,
+ line: lineNum,
+ evidenceSnippet: lineStr.trim()
+ };
+ }
+ }
+ }
+ }
+
+ // 2. Authentication in API routes
+ if (textLower.includes('every api route must authenticate') || textLower.includes('authentication')) {
+ for (const diff of fileDiffs) {
+ if (
+ diff.newPath.includes('/api/') ||
+ diff.newPath.includes('app/api') ||
+ diff.newPath.endsWith('route.ts') ||
+ diff.newPath.endsWith('route.js')
+ ) {
+ const fullPath = resolveFile(diff.newPath);
+ if (!fullPath) continue;
+ const content = fs.readFileSync(fullPath, 'utf8');
+
+ const hasAuth =
+ /\b(auth|session|getServerSession|verifyToken|jwt\.verify|req\.user|req\.session|requireAuth|authenticate)\b/i.test(
+ content
+ );
+
+ if (!hasAuth) {
+ return {
+ rule,
+ status: 'VIOLATION',
+ detectedMessage: `API route "${diff.newPath}" does not appear to contain user authentication checks.`,
+ file: diff.newPath,
+ line: diff.addedLines[0] || 1,
+ evidenceSnippet: `Route file: ${diff.newPath}`
+ };
+ }
+ }
+ }
+ }
+
+ // 3. Ownership check on orders/user resources
+ if (
+ textLower.includes('own orders') ||
+ textLower.includes('ownership') ||
+ textLower.includes('own resources')
+ ) {
+ for (const diff of fileDiffs) {
+ if (diff.newPath.includes('order') || diff.newPath.includes('orders')) {
+ const fullPath = resolveFile(diff.newPath);
+ if (!fullPath) continue;
+ const content = fs.readFileSync(fullPath, 'utf8');
+
+ if (
+ /findUnique|findById|findOne/i.test(content) &&
+ !/userId|user\.id|session\.user/i.test(content)
+ ) {
+ return {
+ rule,
+ status: 'VIOLATION',
+ detectedMessage: `Query in "${diff.newPath}" queries orders without verifying userId/ownership constraint.`,
+ file: diff.newPath,
+ line: diff.addedLines[0] || 1,
+ evidenceSnippet: `Resource: ${diff.newPath}`
+ };
+ }
+ }
+ }
+ }
+
+ // 4. Test requirement for API endpoints
+ if (textLower.includes('require tests') || textLower.includes('api endpoints require tests')) {
+ for (const diff of fileDiffs) {
+ if (
+ (diff.newPath.includes('/api/') || diff.newPath.endsWith('route.ts')) &&
+ !graph.isTestFile(diff.newPath)
+ ) {
+ const baseName = path.basename(diff.newPath, path.extname(diff.newPath));
+ const hasTest = [
+ `${diff.newPath.replace(/\.(ts|js)$/, '.test.$1')}`,
+ `${diff.newPath.replace(/\.(ts|js)$/, '.spec.$1')}`,
+ path.join(path.dirname(diff.newPath), '__tests__', `${baseName}.test.ts`)
+ ].some((p) => Boolean(resolveFile(p)));
+
+ if (!hasTest) {
+ return {
+ rule,
+ status: 'WARNING',
+ detectedMessage: `Modified API endpoint "${diff.newPath}" has no co-located or associated test file.`,
+ file: diff.newPath,
+ line: diff.addedLines[0] || 1,
+ evidenceSnippet: `Missing test for: ${diff.newPath}`
+ };
+ }
+ }
+ }
+ }
+
+ // 5. Payment idempotency
+ if (textLower.includes('idempotent') || textLower.includes('payment')) {
+ for (const diff of fileDiffs) {
+ if (diff.newPath.includes('payment') || diff.newPath.includes('checkout')) {
+ const fullPath = resolveFile(diff.newPath);
+ if (!fullPath) continue;
+ const content = fs.readFileSync(fullPath, 'utf8');
+
+ if (
+ /post|charge|pay/i.test(content) &&
+ !/idempotency|idempotent|idempotency-key/i.test(content)
+ ) {
+ return {
+ rule,
+ status: 'WARNING',
+ detectedMessage: `Payment processing file "${diff.newPath}" does not specify an idempotency key or idempotency check.`,
+ file: diff.newPath,
+ line: diff.addedLines[0] || 1,
+ evidenceSnippet: `Payment handler: ${diff.newPath}`
+ };
+ }
+ }
+ }
+ }
+
+ // Passed
+ return {
+ rule,
+ status: 'PASS'
+ };
+ }
+}
diff --git a/src/rules/riskEngine.ts b/src/rules/riskEngine.ts
new file mode 100644
index 0000000..dc5d811
--- /dev/null
+++ b/src/rules/riskEngine.ts
@@ -0,0 +1,608 @@
+import * as fs from 'node:fs';
+import * as ts from 'typescript';
+import { EvidenceTraceStep, FileDiff, RiskFinding, RiskSeverity } from '../types';
+
+export class RiskEngine {
+ /**
+ * Runs deterministic static risk detection on changed files and their diffs.
+ */
+ public static analyzeFile(
+ filePath: string,
+ content?: string,
+ fileDiff?: FileDiff
+ ): RiskFinding[] {
+ if (!/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filePath)) {
+ return [];
+ }
+
+ const sourceText = content ?? (fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '');
+ if (!sourceText) return [];
+
+ const isJsx = filePath.endsWith('.tsx') || filePath.endsWith('.jsx');
+ const sourceFile = ts.createSourceFile(
+ filePath,
+ sourceText,
+ ts.ScriptTarget.Latest,
+ true,
+ isJsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS
+ );
+
+ const findings: RiskFinding[] = [];
+ const changedLinesSet = fileDiff ? new Set(fileDiff.addedLines) : null;
+
+ const getLineAndColumn = (pos: number) => {
+ const lc = sourceFile.getLineAndCharacterOfPosition(pos);
+ return { line: lc.line + 1, column: lc.character + 1 };
+ };
+
+ const getSnippet = (line: number): string => {
+ const lines = sourceText.split('\n');
+ const start = Math.max(0, line - 2);
+ const end = Math.min(lines.length, line + 1);
+ return lines.slice(start, end).join('\n');
+ };
+
+ const isLineChanged = (line: number): boolean => {
+ if (!changedLinesSet) return true;
+ return changedLinesSet.has(line);
+ };
+
+ const isTest = this.isTestFile(filePath);
+
+ // 1. Text & Regex analysis for Hardcoded Secrets (skip test fixtures)
+ if (!isTest) {
+ findings.push(...this.checkHardcodedSecrets(filePath, sourceText, isLineChanged, getSnippet));
+ }
+
+ // 2. AST Traversals for semantic rules
+ const isApiRoute =
+ /[\\/]app[\\/].*[\\/]route\.(?:ts|js|tsx|jsx)$/.test(filePath) ||
+ /[\\/]pages[\\/]api[\\/].*\.(?:ts|js|tsx|jsx)$/.test(filePath) ||
+ filePath.includes('api/') ||
+ filePath.includes('routes/');
+
+ const visit = (node: ts.Node) => {
+ // Rule: SQL Injection Detection
+ if (ts.isCallExpression(node)) {
+ const text = node.expression.getText(sourceFile);
+ const isDbQuery =
+ /(\bquery|\$queryRawUnsafe|\bexecute|\braw|\bexecSql)$/i.test(text) ||
+ /\bdb\..*query/i.test(text);
+
+ if (isDbQuery && node.arguments.length > 0) {
+ const firstArg = node.arguments[0];
+ const { line, column } = getLineAndColumn(firstArg.getStart(sourceFile));
+
+ if (isLineChanged(line)) {
+ // Check template literal with expressions: `SELECT * FROM users WHERE id = ${id}`
+ if (ts.isTemplateExpression(firstArg) && firstArg.templateSpans.length > 0) {
+ const snippet = getSnippet(line);
+ findings.push({
+ id: `PC-SQL-${filePath}-${line}`,
+ ruleId: 'PC-SEC-001',
+ category: 'security',
+ title: 'Potential SQL Injection (Template Literal)',
+ severity: 'HIGH',
+ description:
+ 'Database query uses unescaped string interpolation with dynamic variables, exposing the endpoint to SQL injection.',
+ file: filePath,
+ line,
+ column,
+ snippet,
+ evidenceTrace: [
+ {
+ file: filePath,
+ line,
+ description: `Call to ${text} constructed with template literal expression: ${firstArg.getText(sourceFile)}`
+ },
+ {
+ file: filePath,
+ line,
+ description: 'No parameterized query or escaping mechanism detected'
+ }
+ ],
+ recommendation:
+ 'Use parameterized queries (e.g. db.query("SELECT ... WHERE id = $1", [id])) or an ORM prepared statement.'
+ });
+ } else if (
+ ts.isBinaryExpression(firstArg) &&
+ firstArg.operatorToken.kind === ts.SyntaxKind.PlusToken
+ ) {
+ // String concatenation: "SELECT * FROM users WHERE id = " + id
+ const snippet = getSnippet(line);
+ findings.push({
+ id: `PC-SQL-${filePath}-${line}`,
+ ruleId: 'PC-SEC-001',
+ category: 'security',
+ title: 'Potential SQL Injection (String Concatenation)',
+ severity: 'HIGH',
+ description:
+ 'Database query constructs SQL via direct string concatenation, creating a severe injection vulnerability.',
+ file: filePath,
+ line,
+ column,
+ snippet,
+ evidenceTrace: [
+ {
+ file: filePath,
+ line,
+ description: `Call to ${text} using string concatenation: ${firstArg.getText(sourceFile)}`
+ }
+ ],
+ recommendation:
+ 'Avoid string concatenation in SQL queries. Use prepared statements or parameterized queries.'
+ });
+ }
+ }
+ }
+ }
+
+ // Rule: Dangerous eval / Function / child_process.exec
+ if (ts.isCallExpression(node)) {
+ const text = node.expression.getText(sourceFile);
+ if (text === 'eval' || text === 'new Function' || text.endsWith('.exec') || text.endsWith('.execSync')) {
+ const { line, column } = getLineAndColumn(node.getStart(sourceFile));
+ if (isLineChanged(line) && node.arguments.length > 0) {
+ const firstArg = node.arguments[0];
+ // If argument is not a simple string literal, it's dynamic
+ if (!ts.isStringLiteral(firstArg)) {
+ findings.push({
+ id: `PC-EVAL-${filePath}-${line}`,
+ ruleId: 'PC-SEC-004',
+ category: 'security',
+ title: 'Dangerous Code/Command Execution',
+ severity: 'HIGH',
+ description: `Invoking ${text} with dynamic arguments can lead to arbitrary code or command execution.`,
+ file: filePath,
+ line,
+ column,
+ snippet: getSnippet(line),
+ evidenceTrace: [
+ {
+ file: filePath,
+ line,
+ description: `Invocation of ${text}(${firstArg.getText(sourceFile)})`
+ }
+ ],
+ recommendation:
+ 'Avoid eval() or dynamic shell execution. Use safe alternative APIs or execFile with static arguments.'
+ });
+ }
+ }
+ }
+ }
+
+ // Rule: Unsafe User Input SSRF / Open Redirect
+ if (ts.isCallExpression(node)) {
+ const text = node.expression.getText(sourceFile);
+ if (
+ text === 'fetch' ||
+ text === 'axios' ||
+ text.startsWith('axios.') ||
+ text.endsWith('res.redirect') ||
+ text.endsWith('redirect')
+ ) {
+ if (node.arguments.length > 0) {
+ const argText = node.arguments[0].getText(sourceFile);
+ const { line, column } = getLineAndColumn(node.getStart(sourceFile));
+ if (
+ isLineChanged(line) &&
+ (argText.includes('req.body') ||
+ argText.includes('req.query') ||
+ argText.includes('req.params') ||
+ argText.includes('params.'))
+ ) {
+ findings.push({
+ id: `PC-SSRF-${filePath}-${line}`,
+ ruleId: 'PC-SEC-005',
+ category: 'security',
+ title: 'Unsafe User-Controlled URL / Potential SSRF',
+ severity: 'MEDIUM',
+ description:
+ 'User request parameter is passed directly into a network request or redirect without explicit validation or allowlisting.',
+ file: filePath,
+ line,
+ column,
+ snippet: getSnippet(line),
+ evidenceTrace: [
+ {
+ file: filePath,
+ line,
+ description: `Call to ${text}(${argText}) with user-controlled input`
+ }
+ ],
+ recommendation:
+ 'Validate target URLs against an explicit allowlist and enforce URL protocol and host checks.'
+ });
+ }
+ }
+ }
+ }
+
+ // Rule: Missing Error Handling on Async Calls
+ if (ts.isAwaitExpression(node) && !isTest) {
+ const { line, column } = getLineAndColumn(node.getStart(sourceFile));
+ if (isLineChanged(line)) {
+ // Check if this await is inside a try-catch block
+ let parent: ts.Node | undefined = node.parent;
+ let insideTry = false;
+ while (parent) {
+ if (ts.isTryStatement(parent)) {
+ insideTry = true;
+ break;
+ }
+ if (
+ ts.isFunctionDeclaration(parent) ||
+ ts.isArrowFunction(parent) ||
+ ts.isMethodDeclaration(parent)
+ ) {
+ break;
+ }
+ parent = parent.parent;
+ }
+
+ if (!insideTry) {
+ // Also check if expression has .catch
+ const exprText = node.expression.getText(sourceFile);
+ if (!exprText.includes('.catch(')) {
+ findings.push({
+ id: `PC-ERR-${filePath}-${line}`,
+ ruleId: 'PC-REL-006',
+ category: 'reliability',
+ title: 'Unhandled Asynchronous Operation',
+ severity: 'LOW',
+ description:
+ 'Async operation is awaited without an enclosing try/catch block or promise catch handler.',
+ file: filePath,
+ line,
+ column,
+ snippet: getSnippet(line),
+ evidenceTrace: [
+ {
+ file: filePath,
+ line,
+ description: `await ${exprText} executed without try/catch wrapper`
+ }
+ ],
+ recommendation:
+ 'Wrap async database and network calls in try/catch to handle potential failures gracefully.'
+ });
+ }
+ }
+ }
+ }
+
+ // Rule: Unhandled Null/Undefined Database Result
+ if (ts.isVariableDeclaration(node) && !isTest) {
+ this.checkUnhandledNullDbResult(node, sourceFile, filePath, isLineChanged, getSnippet, findings);
+ }
+
+ ts.forEachChild(node, visit);
+ };
+
+ ts.forEachChild(sourceFile, visit);
+
+ // Rule: Missing Authorization in API routes performing DB queries
+ if (isApiRoute) {
+ const authFindings = this.checkApiRouteAuthorization(
+ filePath,
+ sourceFile,
+ sourceText,
+ isLineChanged,
+ getSnippet
+ );
+ findings.push(...authFindings);
+ }
+
+ // Deduplicate findings by ID
+ const uniqueMap = new Map();
+ for (const f of findings) {
+ uniqueMap.set(f.id, f);
+ }
+
+ return Array.from(uniqueMap.values());
+ }
+
+ private static checkHardcodedSecrets(
+ filePath: string,
+ sourceText: string,
+ isLineChanged: (line: number) => boolean,
+ getSnippet: (line: number) => string
+ ): RiskFinding[] {
+ const findings: RiskFinding[] = [];
+ const lines = sourceText.split('\n');
+
+ const patterns = [
+ {
+ regex: /(?:AKIA[0-9A-Z]{16})/,
+ name: 'AWS Access Key ID',
+ ruleId: 'PC-SEC-002',
+ severity: 'HIGH' as RiskSeverity
+ },
+ {
+ regex: /-----BEGIN(?:[A-Z\s]+)?PRIVATE KEY-----/,
+ name: 'Private Encryption Key',
+ ruleId: 'PC-SEC-002',
+ severity: 'HIGH' as RiskSeverity
+ },
+ {
+ regex: /(?:ghp_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9_]{50,})/,
+ name: 'GitHub Personal Access Token',
+ ruleId: 'PC-SEC-002',
+ severity: 'HIGH' as RiskSeverity
+ },
+ {
+ regex: /(?:sk-[a-zA-Z0-9]{32,})/,
+ name: 'OpenAI API Secret Key',
+ ruleId: 'PC-SEC-002',
+ severity: 'HIGH' as RiskSeverity
+ },
+ {
+ regex: /(?:password|apiKey|api_key|client_secret|private_key)\s*[:=]\s*["']([A-Za-z0-9+/=_\-!@#$%^&*]{16,})["']/i,
+ name: 'Hardcoded Secret Credential',
+ ruleId: 'PC-SEC-002',
+ severity: 'HIGH' as RiskSeverity
+ }
+ ];
+
+ lines.forEach((lineText, idx) => {
+ const lineNum = idx + 1;
+ if (!isLineChanged(lineNum)) return;
+
+ // Skip process.env or commented-out lines
+ if (lineText.includes('process.env.') || /^\s*\/\//.test(lineText)) return;
+
+ for (const pattern of patterns) {
+ if (pattern.regex.test(lineText)) {
+ findings.push({
+ id: `PC-SECRET-${filePath}-${lineNum}`,
+ ruleId: pattern.ruleId,
+ category: 'security',
+ title: `Hardcoded Secret Detected (${pattern.name})`,
+ severity: pattern.severity,
+ description: `Potential hardcoded secret or credential found in source code: ${pattern.name}.`,
+ file: filePath,
+ line: lineNum,
+ snippet: getSnippet(lineNum),
+ evidenceTrace: [
+ {
+ file: filePath,
+ line: lineNum,
+ description: `Sensitive literal matched pattern for ${pattern.name}`
+ }
+ ],
+ recommendation:
+ 'Never commit credentials to source control. Move the secret to environment variables or secret manager.'
+ });
+ break;
+ }
+ }
+ });
+
+ return findings;
+ }
+
+ private static checkApiRouteAuthorization(
+ filePath: string,
+ sourceFile: ts.SourceFile,
+ sourceText: string,
+ isLineChanged: (line: number) => boolean,
+ getSnippet: (line: number) => string
+ ): RiskFinding[] {
+ const findings: RiskFinding[] = [];
+
+ // Check if the file imports or uses actual auth utilities (stripping comments)
+ const codeWithoutComments = sourceText.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
+ const hasAuthCheck =
+ /\b(auth|session|getServerSession|verifyToken|jwt\.verify|req\.user|req\.session|requireAuth|authenticate|useSession)\b/i.test(
+ codeWithoutComments
+ );
+
+ // Look for route handler functions
+ const visit = (node: ts.Node) => {
+ let isHandler = false;
+ let handlerName = '';
+
+ if (ts.isFunctionDeclaration(node) && node.name) {
+ handlerName = node.name.text;
+ isHandler = /^(GET|POST|PUT|DELETE|PATCH)$/i.test(handlerName) || handlerName === 'handler';
+ } else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
+ handlerName = node.name.text;
+ isHandler = /^(GET|POST|PUT|DELETE|PATCH)$/i.test(handlerName);
+ }
+
+ if (isHandler && node) {
+ const handlerText = node.getText(sourceFile);
+ const codeInHandler = handlerText.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
+
+ const hasDbQuery =
+ /\b(db|prisma|order|user|account|payment)(\.[a-zA-Z0-9_$]+)*\.(find|create|update|delete|query|findUnique|findMany)\b/i.test(
+ codeInHandler
+ );
+
+ const hasLocalAuth =
+ /\b(auth|session|getServerSession|req\.user|verifyToken|jwt|requireAuth)\b/i.test(
+ codeInHandler
+ );
+
+ if (hasDbQuery && !hasLocalAuth && !hasAuthCheck) {
+ const lc = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
+ const line = lc.line + 1;
+
+ if (isLineChanged(line)) {
+ const trace: EvidenceTraceStep[] = [
+ {
+ file: filePath,
+ line,
+ symbol: handlerName,
+ description: `Route handler ${handlerName} receives client request`
+ },
+ {
+ file: filePath,
+ line,
+ description: 'Handler performs database query / resource mutation'
+ },
+ {
+ file: filePath,
+ line,
+ description: 'NO authentication/authorization or session ownership check found in handler body'
+ }
+ ];
+
+ findings.push({
+ id: `PC-AUTH-${filePath}-${line}`,
+ ruleId: 'PC-AUTH-001',
+ category: 'authorization',
+ title: `Missing Authorization Check in API Route (${handlerName})`,
+ severity: 'HIGH',
+ description: `API route handler '${handlerName}' accesses database resources without verifying user authentication or permissions.`,
+ file: filePath,
+ line,
+ snippet: getSnippet(line),
+ evidenceTrace: trace,
+ recommendation:
+ 'Verify user identity and ownership before querying or modifying sensitive records (e.g. const session = await auth()).'
+ });
+ }
+ }
+ }
+
+ ts.forEachChild(node, visit);
+ };
+
+ ts.forEachChild(sourceFile, visit);
+ return findings;
+ }
+
+ private static checkUnhandledNullDbResult(
+ node: ts.VariableDeclaration,
+ sourceFile: ts.SourceFile,
+ filePath: string,
+ isLineChanged: (line: number) => boolean,
+ getSnippet: (line: number) => string,
+ findings: RiskFinding[]
+ ): void {
+ if (!ts.isIdentifier(node.name) || !node.initializer) return;
+ const varName = node.name.text;
+ const initText = node.initializer.getText(sourceFile);
+
+ const isSingleEntityQuery =
+ /\b(db|prisma|repository|repo|order|user|account)(\.[a-zA-Z0-9_$]+)*\.(findUnique|findOne|findById)\b/i.test(
+ initText
+ );
+
+ if (!isSingleEntityQuery) return;
+
+ let scopeNode: ts.Node | undefined = node.parent;
+ while (scopeNode && !ts.isBlock(scopeNode) && !ts.isSourceFile(scopeNode)) {
+ scopeNode = scopeNode.parent;
+ }
+ if (!scopeNode) return;
+
+ const statements = ts.isBlock(scopeNode)
+ ? scopeNode.statements
+ : (scopeNode as ts.SourceFile).statements;
+
+ const varDeclIndex = statements.findIndex((s) => s.getText(sourceFile).includes(varName));
+ if (varDeclIndex === -1) return;
+
+ let hasNullCheck = false;
+ let unsafeDereference: { line: number; text: string } | null = null;
+
+ for (let i = varDeclIndex + 1; i < statements.length; i++) {
+ const stmt = statements[i];
+
+ if (
+ ts.isIfStatement(stmt) &&
+ (new RegExp(`!\\s*${varName}\\b`).test(stmt.expression.getText(sourceFile)) ||
+ new RegExp(`${varName}\\s*(===|==)\\s*(null|undefined)`).test(
+ stmt.expression.getText(sourceFile)
+ ))
+ ) {
+ hasNullCheck = true;
+ break;
+ }
+
+ const checkNode = (n: ts.Node) => {
+ if (hasNullCheck || unsafeDereference) return;
+ if (ts.isPropertyAccessExpression(n)) {
+ if (
+ ts.isIdentifier(n.expression) &&
+ n.expression.text === varName &&
+ !n.questionDotToken
+ ) {
+ let p: ts.Node | undefined = n.parent;
+ let guarded = false;
+ while (p && p !== stmt) {
+ if (
+ ts.isBinaryExpression(p) &&
+ p.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken &&
+ p.left.getText(sourceFile).includes(varName)
+ ) {
+ guarded = true;
+ break;
+ }
+ p = p.parent;
+ }
+
+ if (!guarded) {
+ const lc = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
+ unsafeDereference = {
+ line: lc.line + 1,
+ text: n.getText(sourceFile)
+ };
+ }
+ }
+ }
+ ts.forEachChild(n, checkNode);
+ };
+
+ checkNode(stmt);
+ if (unsafeDereference) break;
+ }
+
+ if (!hasNullCheck && unsafeDereference) {
+ const declLc = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
+ const declLine = declLc.line + 1;
+ const refLine = (unsafeDereference as { line: number; text: string }).line;
+ const refText = (unsafeDereference as { line: number; text: string }).text;
+
+ if (isLineChanged(declLine) || isLineChanged(refLine)) {
+ findings.push({
+ id: `PC-NULL-${filePath}-${refLine}`,
+ ruleId: 'PC-DATA-001',
+ category: 'reliability',
+ title: `Unhandled Null/Undefined Database Result (${varName})`,
+ severity: 'MEDIUM',
+ description: `Database query result '${varName}' is dereferenced as '${refText}' without verifying if the record exists, risking runtime TypeError if not found.`,
+ file: filePath,
+ line: refLine,
+ snippet: getSnippet(refLine),
+ evidenceTrace: [
+ {
+ file: filePath,
+ line: declLine,
+ description: `Record looked up via ${initText}`
+ },
+ {
+ file: filePath,
+ line: refLine,
+ description: `Property accessed directly via '${refText}' without null guard (e.g. if (!${varName}) or ${varName}?.${refText.split('.')[1] || ''})`
+ }
+ ],
+ recommendation: `Check if '${varName}' is null before accessing properties, or use optional chaining (${varName}?.${refText.split('.')[1] || ''}).`
+ });
+ }
+ }
+ }
+
+ public static isTestFile(filePath: string): boolean {
+ const normalized = filePath.replace(/\\/g, '/');
+ return (
+ normalized.includes('.test.') ||
+ normalized.includes('.spec.') ||
+ normalized.includes('/__tests__/') ||
+ normalized.includes('/tests/')
+ );
+ }
+}
diff --git a/src/test-runner/testRunner.ts b/src/test-runner/testRunner.ts
new file mode 100644
index 0000000..11aedfc
--- /dev/null
+++ b/src/test-runner/testRunner.ts
@@ -0,0 +1,165 @@
+import { exec } from 'node:child_process';
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import { promisify } from 'node:util';
+import { CheckResult } from '../types';
+
+const execAsync = promisify(exec);
+
+export interface TestRunnerOptions {
+ timeoutMs?: number;
+}
+
+export class TestRunner {
+ private timeoutMs: number;
+
+ constructor(options: TestRunnerOptions = {}) {
+ this.timeoutMs = options.timeoutMs ?? 45000; // 45 seconds default timeout
+ }
+
+ /**
+ * Detects package manager based on lockfiles
+ */
+ public detectPackageManager(workspaceRoot: string): 'pnpm' | 'yarn' | 'bun' | 'npm' {
+ if (fs.existsSync(path.join(workspaceRoot, 'pnpm-lock.yaml'))) return 'pnpm';
+ if (fs.existsSync(path.join(workspaceRoot, 'yarn.lock'))) return 'yarn';
+ if (
+ fs.existsSync(path.join(workspaceRoot, 'bun.lockb')) ||
+ fs.existsSync(path.join(workspaceRoot, 'bun.lock'))
+ ) {
+ return 'bun';
+ }
+ return 'npm';
+ }
+
+ private getPackageScripts(workspaceRoot: string): Record {
+ const pkgPath = path.join(workspaceRoot, 'package.json');
+ if (!fs.existsSync(pkgPath)) return {};
+ try {
+ const data = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
+ return data.scripts || {};
+ } catch {
+ return {};
+ }
+ }
+
+ private async executeCommand(
+ cmd: string,
+ cwd: string,
+ name: string
+ ): Promise {
+ const startTime = Date.now();
+ try {
+ const { stdout } = await execAsync(cmd, {
+ cwd,
+ timeout: this.timeoutMs,
+ maxBuffer: 5 * 1024 * 1024
+ });
+
+ return {
+ name,
+ status: 'PASS',
+ durationMs: Date.now() - startTime,
+ message: 'Completed successfully without errors.',
+ details: stdout.slice(0, 500)
+ };
+ } catch (err: unknown) {
+ const execError = err as { stdout?: string; stderr?: string; message?: string };
+ const output = `${execError.stdout || ''}\n${execError.stderr || ''}`.trim();
+
+ return {
+ name,
+ status: 'FAIL',
+ durationMs: Date.now() - startTime,
+ message: 'Checks reported errors.',
+ details: output.slice(0, 1000) || execError.message
+ };
+ }
+ }
+
+ public async runTypeScriptCheck(workspaceRoot: string): Promise {
+ const tsconfigPath = path.join(workspaceRoot, 'tsconfig.json');
+ if (!fs.existsSync(tsconfigPath)) {
+ return {
+ name: 'TypeScript',
+ status: 'SKIPPED',
+ message: 'No tsconfig.json found in project root.'
+ };
+ }
+
+ const scripts = this.getPackageScripts(workspaceRoot);
+ const pm = this.detectPackageManager(workspaceRoot);
+
+ let cmd = 'npx tsc --noEmit';
+ if (scripts['typecheck']) {
+ cmd = `${pm} run typecheck`;
+ } else if (scripts['type-check']) {
+ cmd = `${pm} run type-check`;
+ }
+
+ return this.executeCommand(cmd, workspaceRoot, 'TypeScript');
+ }
+
+ public async runLint(workspaceRoot: string): Promise {
+ const scripts = this.getPackageScripts(workspaceRoot);
+ const pm = this.detectPackageManager(workspaceRoot);
+
+ if (scripts['lint']) {
+ return this.executeCommand(`${pm} run lint`, workspaceRoot, 'ESLint');
+ }
+
+ const hasEslint =
+ fs.existsSync(path.join(workspaceRoot, '.eslintrc.json')) ||
+ fs.existsSync(path.join(workspaceRoot, '.eslintrc.js')) ||
+ fs.existsSync(path.join(workspaceRoot, 'eslint.config.js')) ||
+ fs.existsSync(path.join(workspaceRoot, 'eslint.config.mjs'));
+
+ if (hasEslint) {
+ return this.executeCommand('npx eslint .', workspaceRoot, 'ESLint');
+ }
+
+ return {
+ name: 'ESLint',
+ status: 'SKIPPED',
+ message: 'No lint script or ESLint configuration detected.'
+ };
+ }
+
+ public async runUnitTests(workspaceRoot: string): Promise {
+ const scripts = this.getPackageScripts(workspaceRoot);
+ const pm = this.detectPackageManager(workspaceRoot);
+
+ if (scripts['test']) {
+ // If the default npm test is "echo \"Error: no test specified\" && exit 1", treat as skipped
+ if (scripts['test'].includes('no test specified')) {
+ return {
+ name: 'Unit Tests',
+ status: 'SKIPPED',
+ message: 'No tests configured in package.json.'
+ };
+ }
+ return this.executeCommand(`${pm} test`, workspaceRoot, 'Unit Tests');
+ }
+
+ return {
+ name: 'Unit Tests',
+ status: 'SKIPPED',
+ message: 'No test script found in package.json.'
+ };
+ }
+
+ public async runBuild(workspaceRoot: string): Promise {
+ const scripts = this.getPackageScripts(workspaceRoot);
+ const pm = this.detectPackageManager(workspaceRoot);
+
+ if (scripts['build']) {
+ return this.executeCommand(`${pm} run build`, workspaceRoot, 'Build Check');
+ }
+
+ return {
+ name: 'Build Check',
+ status: 'SKIPPED',
+ message: 'No build script found in package.json.'
+ };
+ }
+}
diff --git a/src/types/index.ts b/src/types/index.ts
new file mode 100644
index 0000000..2369a7c
--- /dev/null
+++ b/src/types/index.ts
@@ -0,0 +1,239 @@
+/**
+ * ProofCode Core Types
+ * AI writes code. ProofCode proves the change.
+ */
+
+export type RiskSeverity = 'HIGH' | 'MEDIUM' | 'LOW';
+
+export type CheckStatus = 'PASS' | 'FAIL' | 'WARNING' | 'SKIPPED';
+
+export type RuleStatus = 'PASS' | 'VIOLATION' | 'WARNING';
+
+export type VerificationVerdict = 'READY_TO_SHIP' | 'NEEDS_REVIEW' | 'BLOCKED';
+
+export interface ChangedLineRange {
+ start: number;
+ end: number;
+}
+
+export interface DiffHunk {
+ oldStart: number;
+ oldLines: number;
+ newStart: number;
+ newLines: number;
+ header: string;
+ lines: string[];
+}
+
+export interface FileDiff {
+ oldPath: string;
+ newPath: string;
+ isNew: boolean;
+ isDeleted: boolean;
+ isRenamed: boolean;
+ hunks: DiffHunk[];
+ addedLines: number[];
+ deletedLines: number[];
+ modifiedLineRanges: ChangedLineRange[];
+}
+
+export interface GitDiffResult {
+ repoRoot: string;
+ baseRef?: string;
+ files: FileDiff[];
+ totalFilesChanged: number;
+ totalInsertions: number;
+ totalDeletions: number;
+}
+
+export interface GitFileStatus {
+ path: string;
+ status: 'modified' | 'added' | 'deleted' | 'untracked' | 'renamed';
+}
+
+export type SymbolKind =
+ | 'function'
+ | 'arrow_function'
+ | 'method'
+ | 'class'
+ | 'interface'
+ | 'type_alias'
+ | 'variable'
+ | 'api_route';
+
+export interface SymbolInfo {
+ name: string;
+ kind: SymbolKind;
+ filePath: string;
+ startLine: number;
+ endLine: number;
+ startColumn: number;
+ endColumn: number;
+ isExported: boolean;
+ isDefaultExport?: boolean;
+ parentSymbol?: string;
+ calls: string[];
+}
+
+export interface ImportInfo {
+ source: string;
+ resolvedPath?: string;
+ specifiers: {
+ name: string;
+ propertyName?: string;
+ isDefault?: boolean;
+ isNamespace?: boolean;
+ }[];
+ line: number;
+}
+
+export interface ExportInfo {
+ name: string;
+ isDefault: boolean;
+ line: number;
+}
+
+export interface FileAnalysis {
+ filePath: string;
+ symbols: SymbolInfo[];
+ imports: ImportInfo[];
+ exports: ExportInfo[];
+ hasApiRoute: boolean;
+ apiRoutes: {
+ method: string;
+ path: string;
+ handlerSymbol: string;
+ line: number;
+ }[];
+}
+
+export interface AffectedCaller {
+ callerName: string;
+ filePath: string;
+ line: number;
+ hasTest: boolean;
+ testFilePath?: string;
+}
+
+export interface AffectedRoute {
+ routePath: string;
+ method: string;
+ filePath: string;
+ line: number;
+}
+
+export interface AffectedSymbol {
+ symbolName: string;
+ kind: SymbolKind;
+ filePath: string;
+ callers: AffectedCaller[];
+ isApiRoute: boolean;
+}
+
+export interface ImpactAnalysis {
+ changedFiles: string[];
+ changedSymbols: AffectedSymbol[];
+ affectedFiles: string[];
+ affectedCallers: AffectedCaller[];
+ affectedRoutes: AffectedRoute[];
+ totalCallersCount: number;
+ untestedCallersCount: number;
+}
+
+export interface EvidenceTraceStep {
+ file: string;
+ line: number;
+ symbol?: string;
+ description: string;
+}
+
+export interface RiskFinding {
+ id: string;
+ ruleId: string;
+ category: 'security' | 'authorization' | 'data_integrity' | 'reliability' | 'quality';
+ title: string;
+ severity: RiskSeverity;
+ description: string;
+ file: string;
+ line: number;
+ column?: number;
+ snippet: string;
+ evidenceTrace: EvidenceTraceStep[];
+ recommendation: string;
+}
+
+export interface ProjectRule {
+ id: string;
+ title: string;
+ description: string;
+ severity: RiskSeverity;
+ category?: string;
+ tags?: string[];
+}
+
+export interface RuleResult {
+ rule: ProjectRule;
+ status: RuleStatus;
+ detectedMessage?: string;
+ file?: string;
+ line?: number;
+ evidenceSnippet?: string;
+}
+
+export interface CheckResult {
+ name: string;
+ status: CheckStatus;
+ durationMs?: number;
+ message?: string;
+ details?: string;
+}
+
+export interface VerificationScore {
+ total: number;
+ deductions: {
+ reason: string;
+ amount: number;
+ }[];
+}
+
+export interface BreakageRiskItem {
+ id: string;
+ severity: RiskSeverity;
+ area: string;
+ trigger: string;
+ detail: string;
+ affectedCallersOrRoutes: string[];
+ evidenceFile?: string;
+ evidenceLine?: number;
+}
+
+export interface VerificationReport {
+ timestamp: string;
+ repoRoot: string;
+ branch?: string;
+ commitHash?: string;
+ score: VerificationScore;
+ verdict: VerificationVerdict;
+ summary: {
+ filesChanged: number;
+ linesAdded: number;
+ linesDeleted: number;
+ symbolsAffected: number;
+ apiRoutesAffected: number;
+ testsAdded: number;
+ testsAffected: number;
+ highRiskCount: number;
+ mediumRiskCount: number;
+ lowRiskCount: number;
+ };
+ impact: ImpactAnalysis;
+ risks: RiskFinding[];
+ breakageRisks?: BreakageRiskItem[];
+ rules: RuleResult[];
+ checks: {
+ typescript: CheckResult;
+ eslint: CheckResult;
+ unitTests: CheckResult;
+ build: CheckResult;
+ };
+}
diff --git a/src/ui/codeActionProvider.ts b/src/ui/codeActionProvider.ts
new file mode 100644
index 0000000..4e32c88
--- /dev/null
+++ b/src/ui/codeActionProvider.ts
@@ -0,0 +1,113 @@
+import * as vscode from 'vscode';
+
+export class ProofCodeCodeActionProvider implements vscode.CodeActionProvider {
+ public static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix];
+
+ public provideCodeActions(
+ document: vscode.TextDocument,
+ _range: vscode.Range | vscode.Selection,
+ context: vscode.CodeActionContext,
+ _token: vscode.CancellationToken
+ ): vscode.CodeAction[] {
+ const actions: vscode.CodeAction[] = [];
+
+ // Filter diagnostics generated by ProofCode
+ const proofCodeDiagnostics = context.diagnostics.filter(
+ (diag) => diag.source === 'ProofCode'
+ );
+
+ for (const diag of proofCodeDiagnostics) {
+ const lineIdx = diag.range.start.line;
+ const line = document.lineAt(lineIdx);
+ const lineText = line.text;
+ const indentMatch = lineText.match(/^\s*/);
+ const indent = indentMatch ? indentMatch[0] : '';
+
+ // 1. Quick Fix for PC-REL-006: Unhandled Async Operation
+ if (diag.code === 'PC-REL-006') {
+ const action = new vscode.CodeAction(
+ '🛡️ ProofCode: Wrap in try/catch block',
+ vscode.CodeActionKind.QuickFix
+ );
+ action.isPreferred = true;
+ action.diagnostics = [diag];
+
+ const edit = new vscode.WorkspaceEdit();
+ const wrapped = `${indent}try {\n${indent} ${lineText.trim()}\n${indent}} catch (error) {\n${indent} console.error('Async operation failed:', error);\n${indent}}`;
+ edit.replace(document.uri, line.range, wrapped);
+ action.edit = edit;
+ actions.push(action);
+ }
+
+ // 2. Quick Fix for PC-DATA-001: Unhandled Null/Undefined Database Result
+ if (diag.code === 'PC-DATA-001') {
+ // Match variable name from diagnostic message or code line
+ const varMatch = diag.message.match(/\(([^)]+)\)/);
+ const varName = varMatch ? varMatch[1] : 'record';
+
+ // Fix A: Add null guard
+ const guardAction = new vscode.CodeAction(
+ `🛡️ ProofCode: Add null guard check (if (!${varName}))`,
+ vscode.CodeActionKind.QuickFix
+ );
+ guardAction.isPreferred = true;
+ guardAction.diagnostics = [diag];
+
+ const guardEdit = new vscode.WorkspaceEdit();
+ const guardText = `${indent}if (!${varName}) throw new Error('${varName} not found');\n`;
+ guardEdit.insert(document.uri, new vscode.Position(lineIdx, 0), guardText);
+ guardAction.edit = guardEdit;
+ actions.push(guardAction);
+
+ // Fix B: Use optional chaining
+ const dotIndex = lineText.indexOf(`${varName}.`);
+ if (dotIndex !== -1) {
+ const optChainAction = new vscode.CodeAction(
+ `🛡️ ProofCode: Use optional chaining (${varName}?.${lineText.slice(dotIndex + varName.length + 1).split(/\W/)[0]})`,
+ vscode.CodeActionKind.QuickFix
+ );
+ optChainAction.diagnostics = [diag];
+
+ const optEdit = new vscode.WorkspaceEdit();
+ const dotRange = new vscode.Range(
+ lineIdx,
+ dotIndex + varName.length,
+ lineIdx,
+ dotIndex + varName.length + 1
+ );
+ optEdit.replace(document.uri, dotRange, '?.');
+ optChainAction.edit = optEdit;
+ actions.push(optChainAction);
+ }
+ }
+
+ // 3. Quick Fix for PC-AUTH-001: Missing Authorization Check in API Route
+ if (diag.code === 'PC-AUTH-001') {
+ const authAction = new vscode.CodeAction(
+ '🛡️ ProofCode: Insert session authentication guard',
+ vscode.CodeActionKind.QuickFix
+ );
+ authAction.isPreferred = true;
+ authAction.diagnostics = [diag];
+
+ const authEdit = new vscode.WorkspaceEdit();
+ const authSnippet = `${indent} const session = await auth();\n${indent} if (!session) return new Response('Unauthorized', { status: 401 });\n`;
+ authEdit.insert(document.uri, new vscode.Position(lineIdx + 1, 0), authSnippet);
+ authAction.edit = authEdit;
+ actions.push(authAction);
+ }
+
+ // 4. Quick Fix for PC-SEC-001: Potential SQL Injection
+ if (diag.code === 'PC-SEC-001') {
+ const sqlAction = new vscode.CodeAction(
+ '🛡️ ProofCode: Review query parameterization guidance',
+ vscode.CodeActionKind.QuickFix
+ );
+ sqlAction.diagnostics = [diag];
+ actions.push(sqlAction);
+ }
+ }
+
+ return actions;
+ }
+}
diff --git a/src/ui/codeLensProvider.ts b/src/ui/codeLensProvider.ts
new file mode 100644
index 0000000..bb1bb5a
--- /dev/null
+++ b/src/ui/codeLensProvider.ts
@@ -0,0 +1,71 @@
+import * as vscode from 'vscode';
+import { VerificationReport } from '../types';
+
+export class ProofCodeCodeLensProvider implements vscode.CodeLensProvider {
+ private report: VerificationReport | null = null;
+ private onDidChangeCodeLensesEmitter = new vscode.EventEmitter();
+ public readonly onDidChangeCodeLenses = this.onDidChangeCodeLensesEmitter.event;
+
+ public setReport(report: VerificationReport | null): void {
+ this.report = report;
+ this.onDidChangeCodeLensesEmitter.fire();
+ }
+
+ public provideCodeLenses(
+ document: vscode.TextDocument,
+ _token: vscode.CancellationToken
+ ): vscode.CodeLens[] {
+ if (!this.report) return [];
+
+ const lenses: vscode.CodeLens[] = [];
+ const docPath = document.fileName.replace(/\\/g, '/');
+
+ // Find symbols in this file from impact analysis
+ const fileSymbols = this.report.impact.changedSymbols.filter(
+ (s) => s.filePath.replace(/\\/g, '/') === docPath || docPath.endsWith(s.filePath)
+ );
+
+ // Find risks in this file
+ const fileRisks = this.report.risks.filter(
+ (r) => r.file.replace(/\\/g, '/') === docPath || docPath.endsWith(r.file)
+ );
+
+ for (const sym of fileSymbols) {
+ const callersCount = sym.callers.length;
+ const untestedCount = sym.callers.filter((c) => !c.hasTest).length;
+
+ // Find line number in document for this symbol
+ for (let i = 0; i < Math.min(document.lineCount, 500); i++) {
+ const lineText = document.lineAt(i).text;
+ if (
+ lineText.includes(`function ${sym.symbolName}`) ||
+ lineText.includes(`const ${sym.symbolName}`) ||
+ lineText.includes(`export async function ${sym.symbolName}`) ||
+ lineText.includes(`export function ${sym.symbolName}`)
+ ) {
+ const range = new vscode.Range(i, 0, i, lineText.length);
+ const relatedRisks = fileRisks.filter((r) => r.line >= i + 1 && r.line <= i + 15);
+
+ let title = `🛡️ ProofCode: ${callersCount} caller${callersCount === 1 ? '' : 's'}`;
+ if (untestedCount > 0) {
+ title += ` (${untestedCount} untested)`;
+ }
+ if (relatedRisks.length > 0) {
+ title += ` | ⚠️ ${relatedRisks.length} risk${relatedRisks.length === 1 ? '' : 's'}`;
+ }
+
+ lenses.push(
+ new vscode.CodeLens(range, {
+ title,
+ command: 'proofcode.openDashboard',
+ tooltip: 'Click to open ProofCode Change Verification Dashboard'
+ })
+ );
+ break;
+ }
+ }
+ }
+
+ return lenses;
+ }
+}
diff --git a/src/ui/treeDataProvider.ts b/src/ui/treeDataProvider.ts
new file mode 100644
index 0000000..d6cd62c
--- /dev/null
+++ b/src/ui/treeDataProvider.ts
@@ -0,0 +1,284 @@
+import * as path from 'node:path';
+import * as vscode from 'vscode';
+import { RiskFinding, RuleResult, VerificationReport } from '../types';
+
+export class ProofCodeTreeItem extends vscode.TreeItem {
+ constructor(
+ public readonly label: string,
+ public readonly collapsibleState: vscode.TreeItemCollapsibleState,
+ public readonly contextValue?: string,
+ public readonly itemData?: unknown
+ ) {
+ super(label, collapsibleState);
+ }
+}
+
+export class ProofCodeTreeDataProvider implements vscode.TreeDataProvider {
+ private _onDidChangeTreeData: vscode.EventEmitter =
+ new vscode.EventEmitter();
+ readonly onDidChangeTreeData: vscode.Event =
+ this._onDidChangeTreeData.event;
+
+ private currentReport: VerificationReport | null = null;
+
+ public setReport(report: VerificationReport | null): void {
+ this.currentReport = report;
+ this._onDidChangeTreeData.fire();
+ }
+
+ public getTreeItem(element: ProofCodeTreeItem): vscode.TreeItem {
+ return element;
+ }
+
+ public async getChildren(element?: ProofCodeTreeItem): Promise {
+ if (!this.currentReport) {
+ return [
+ new ProofCodeTreeItem(
+ 'Run "ProofCode: Verify Current Changes" to inspect diff',
+ vscode.TreeItemCollapsibleState.None
+ )
+ ];
+ }
+
+ const report = this.currentReport;
+
+ if (!element) {
+ // Root level items
+ const scoreBadge =
+ report.verdict === 'READY_TO_SHIP'
+ ? '🟢 Ready to Ship'
+ : report.verdict === 'NEEDS_REVIEW'
+ ? '🟠 Needs Review'
+ : '🔴 Blocked';
+
+ const scoreItem = new ProofCodeTreeItem(
+ `Verification Score: ${report.score.total}% (${scoreBadge})`,
+ vscode.TreeItemCollapsibleState.None,
+ 'score'
+ );
+ scoreItem.iconPath = new vscode.ThemeIcon('shield');
+
+ const filesItem = new ProofCodeTreeItem(
+ `Changed Files (${report.summary.filesChanged})`,
+ vscode.TreeItemCollapsibleState.Collapsed,
+ 'changed_files'
+ );
+ filesItem.iconPath = new vscode.ThemeIcon('files');
+
+ const impactItem = new ProofCodeTreeItem(
+ `Impact (${report.summary.symbolsAffected} symbols, ${report.summary.apiRoutesAffected} routes)`,
+ vscode.TreeItemCollapsibleState.Collapsed,
+ 'impact'
+ );
+ impactItem.iconPath = new vscode.ThemeIcon('zap');
+
+ const totalRisks = report.risks.length;
+ const risksItem = new ProofCodeTreeItem(
+ `Risks & Evidence (${totalRisks})`,
+ vscode.TreeItemCollapsibleState.Expanded,
+ 'risks'
+ );
+ risksItem.iconPath = new vscode.ThemeIcon(
+ report.summary.highRiskCount > 0 ? 'error' : 'warning'
+ );
+
+ const rulesItem = new ProofCodeTreeItem(
+ `Project Rules (${report.rules.filter((r) => r.status === 'PASS').length} PASS / ${
+ report.rules.filter((r) => r.status === 'VIOLATION').length
+ } Violations)`,
+ vscode.TreeItemCollapsibleState.Collapsed,
+ 'rules'
+ );
+ rulesItem.iconPath = new vscode.ThemeIcon('checklist');
+
+ const checksItem = new ProofCodeTreeItem(
+ `Checks & Tests (TS: ${report.checks.typescript.status})`,
+ vscode.TreeItemCollapsibleState.Collapsed,
+ 'checks'
+ );
+ checksItem.iconPath = new vscode.ThemeIcon('beaker');
+
+ const items = [scoreItem, filesItem, impactItem];
+
+ if (report.breakageRisks && report.breakageRisks.length > 0) {
+ const breakageItem = new ProofCodeTreeItem(
+ `What Could Break (${report.breakageRisks.length})`,
+ vscode.TreeItemCollapsibleState.Expanded,
+ 'breakage'
+ );
+ breakageItem.iconPath = new vscode.ThemeIcon('flame');
+ items.push(breakageItem);
+ }
+
+ items.push(risksItem, rulesItem, checksItem);
+ return items;
+ }
+
+ // Children of Changed Files
+ if (element.contextValue === 'changed_files') {
+ return report.impact.changedFiles.map((file) => {
+ const baseName = path.basename(file);
+ const item = new ProofCodeTreeItem(baseName, vscode.TreeItemCollapsibleState.None);
+ item.description = path.relative(report.repoRoot, file);
+ item.iconPath = new vscode.ThemeIcon('file-code');
+ item.command = {
+ command: 'vscode.open',
+ title: 'Open File',
+ arguments: [vscode.Uri.file(file)]
+ };
+ return item;
+ });
+ }
+
+ // Children of Impact
+ if (element.contextValue === 'impact') {
+ const items: ProofCodeTreeItem[] = [];
+
+ for (const sym of report.impact.changedSymbols) {
+ const item = new ProofCodeTreeItem(
+ `${sym.symbolName} (${sym.callers.length} callers)`,
+ vscode.TreeItemCollapsibleState.None
+ );
+ item.description = path.basename(sym.filePath);
+ item.iconPath = new vscode.ThemeIcon('symbol-function');
+ items.push(item);
+ }
+
+ for (const route of report.impact.affectedRoutes) {
+ const item = new ProofCodeTreeItem(
+ `${route.method} ${path.basename(route.routePath)}`,
+ vscode.TreeItemCollapsibleState.None
+ );
+ item.description = 'API Route';
+ item.iconPath = new vscode.ThemeIcon('globe');
+ items.push(item);
+ }
+
+ if (items.length === 0) {
+ return [
+ new ProofCodeTreeItem('No external impact detected', vscode.TreeItemCollapsibleState.None)
+ ];
+ }
+
+ return items;
+ }
+
+ // Children of What Could Break
+ if (element.contextValue === 'breakage') {
+ return (report.breakageRisks || []).map((b) => {
+ const item = new ProofCodeTreeItem(
+ `[${b.severity}] ${b.area}: ${b.trigger}`,
+ vscode.TreeItemCollapsibleState.None
+ );
+ item.description = b.detail;
+ item.tooltip = `${b.detail}\nDependents: ${b.affectedCallersOrRoutes.join(', ')}`;
+ item.iconPath = new vscode.ThemeIcon(
+ b.severity === 'HIGH' ? 'error' : b.severity === 'MEDIUM' ? 'warning' : 'info'
+ );
+ if (b.evidenceFile) {
+ item.command = {
+ command: 'proofcode.openEvidence',
+ title: 'Open Evidence',
+ arguments: [b.evidenceFile, b.evidenceLine || 1]
+ };
+ }
+ return item;
+ });
+ }
+
+ // Children of Risks
+ if (element.contextValue === 'risks') {
+ if (report.risks.length === 0) {
+ const item = new ProofCodeTreeItem(
+ '✓ No static risks detected in changes',
+ vscode.TreeItemCollapsibleState.None
+ );
+ item.iconPath = new vscode.ThemeIcon('pass');
+ return [item];
+ }
+
+ return report.risks.map((risk: RiskFinding) => {
+ const icon =
+ risk.severity === 'HIGH'
+ ? 'error'
+ : risk.severity === 'MEDIUM'
+ ? 'warning'
+ : 'info';
+
+ const item = new ProofCodeTreeItem(
+ `[${risk.severity}] ${risk.title}`,
+ vscode.TreeItemCollapsibleState.None,
+ 'risk_item',
+ risk
+ );
+ item.description = `${path.basename(risk.file)}:${risk.line}`;
+ item.tooltip = `${risk.description}\n\nEvidence:\n${risk.evidenceTrace
+ .map((t) => `${t.line}: ${t.description}`)
+ .join('\n')}\n\nRecommendation: ${risk.recommendation}`;
+ item.iconPath = new vscode.ThemeIcon(icon);
+ item.command = {
+ command: 'proofcode.openEvidence',
+ title: 'Open Evidence',
+ arguments: [risk.file, risk.line]
+ };
+ return item;
+ });
+ }
+
+ // Children of Rules
+ if (element.contextValue === 'rules') {
+ return report.rules.map((ruleResult: RuleResult) => {
+ const item = new ProofCodeTreeItem(
+ `[${ruleResult.status}] ${ruleResult.rule.title}`,
+ vscode.TreeItemCollapsibleState.None
+ );
+ item.description = ruleResult.detectedMessage || ruleResult.rule.description;
+ item.iconPath = new vscode.ThemeIcon(
+ ruleResult.status === 'PASS'
+ ? 'pass'
+ : ruleResult.status === 'VIOLATION'
+ ? 'error'
+ : 'warning'
+ );
+ if (ruleResult.file && ruleResult.line) {
+ item.command = {
+ command: 'proofcode.openEvidence',
+ title: 'Open Violation Location',
+ arguments: [ruleResult.file, ruleResult.line]
+ };
+ }
+ return item;
+ });
+ }
+
+ // Children of Checks
+ if (element.contextValue === 'checks') {
+ const items: ProofCodeTreeItem[] = [];
+ const checks = [
+ report.checks.typescript,
+ report.checks.eslint,
+ report.checks.unitTests,
+ report.checks.build
+ ];
+
+ for (const check of checks) {
+ const item = new ProofCodeTreeItem(
+ `${check.name}: ${check.status}`,
+ vscode.TreeItemCollapsibleState.None
+ );
+ item.description = check.message;
+ item.iconPath = new vscode.ThemeIcon(
+ check.status === 'PASS'
+ ? 'pass'
+ : check.status === 'FAIL'
+ ? 'error'
+ : 'debug-pause'
+ );
+ items.push(item);
+ }
+ return items;
+ }
+
+ return [];
+ }
+}
diff --git a/src/ui/webviewPanel.ts b/src/ui/webviewPanel.ts
new file mode 100644
index 0000000..c570c34
--- /dev/null
+++ b/src/ui/webviewPanel.ts
@@ -0,0 +1,1371 @@
+import * as path from 'node:path';
+import * as vscode from 'vscode';
+import { VerificationReport } from '../types';
+
+export class ProofCodeWebviewPanel {
+ public static currentPanel: ProofCodeWebviewPanel | undefined;
+ private readonly _panel: vscode.WebviewPanel;
+ private readonly _extensionUri: vscode.Uri;
+ private _disposables: vscode.Disposable[] = [];
+ private _report: VerificationReport | null = null;
+ private _onVerifyRequested: (() => Promise) | null = null;
+ private _onInitRulesRequested: (() => Promise) | null = null;
+
+ private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
+ this._panel = panel;
+ this._extensionUri = extensionUri;
+
+ this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
+
+ this._panel.webview.onDidReceiveMessage(
+ async (message) => {
+ switch (message.type) {
+ case 'verify':
+ if (this._onVerifyRequested) {
+ await this._onVerifyRequested();
+ }
+ break;
+ case 'openEvidence':
+ if (message.file && message.line) {
+ await this.openFileAtLine(message.file, message.line);
+ }
+ break;
+ case 'initRules':
+ if (this._onInitRulesRequested) {
+ await this._onInitRulesRequested();
+ }
+ break;
+ case 'exportMarkdown':
+ await vscode.commands.executeCommand('proofcode.exportReport');
+ break;
+ }
+ },
+ null,
+ this._disposables
+ );
+ }
+
+ public getExtensionUri(): vscode.Uri {
+ return this._extensionUri;
+ }
+
+ public static createOrShow(
+ extensionUri: vscode.Uri,
+ report: VerificationReport | null,
+ onVerify: () => Promise,
+ onInitRules: () => Promise
+ ): ProofCodeWebviewPanel {
+ const column = vscode.window.activeTextEditor
+ ? vscode.window.activeTextEditor.viewColumn
+ : undefined;
+
+ if (ProofCodeWebviewPanel.currentPanel) {
+ ProofCodeWebviewPanel.currentPanel._panel.reveal(column);
+ ProofCodeWebviewPanel.currentPanel.setReport(report);
+ ProofCodeWebviewPanel.currentPanel._onVerifyRequested = onVerify;
+ ProofCodeWebviewPanel.currentPanel._onInitRulesRequested = onInitRules;
+ return ProofCodeWebviewPanel.currentPanel;
+ }
+
+ const panel = vscode.window.createWebviewPanel(
+ 'proofcodeDashboard',
+ 'ProofCode Verification',
+ column || vscode.ViewColumn.One,
+ {
+ enableScripts: true,
+ retainContextWhenHidden: true,
+ localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'resources')]
+ }
+ );
+
+ ProofCodeWebviewPanel.currentPanel = new ProofCodeWebviewPanel(panel, extensionUri);
+ ProofCodeWebviewPanel.currentPanel._onVerifyRequested = onVerify;
+ ProofCodeWebviewPanel.currentPanel._onInitRulesRequested = onInitRules;
+ ProofCodeWebviewPanel.currentPanel.setReport(report);
+
+ return ProofCodeWebviewPanel.currentPanel;
+ }
+
+ public setReport(report: VerificationReport | null): void {
+ this._report = report;
+ this._panel.webview.html = this._getHtmlForWebview(report);
+ }
+
+ private async openFileAtLine(filePath: string, line: number): Promise {
+ try {
+ const fullPath = path.isAbsolute(filePath)
+ ? filePath
+ : path.resolve(this._report?.repoRoot || '', filePath);
+ const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(fullPath));
+ const editor = await vscode.window.showTextDocument(doc);
+ const position = new vscode.Position(Math.max(0, line - 1), 0);
+ editor.selection = new vscode.Selection(position, position);
+ editor.revealRange(
+ new vscode.Range(position, position),
+ vscode.TextEditorRevealType.InCenter
+ );
+ } catch (err: unknown) {
+ vscode.window.showErrorMessage(`Failed to open evidence file: ${(err as Error).message}`);
+ }
+ }
+
+ public dispose(): void {
+ ProofCodeWebviewPanel.currentPanel = undefined;
+ this._panel.dispose();
+ while (this._disposables.length) {
+ const x = this._disposables.pop();
+ if (x) {
+ x.dispose();
+ }
+ }
+ }
+
+ private _getHtmlForWebview(report: VerificationReport | null): string {
+ const score = report?.score.total ?? 0;
+ const verdict = report?.verdict ?? 'NEEDS_REVIEW';
+ const verdictText =
+ verdict === 'READY_TO_SHIP'
+ ? 'READY TO SHIP'
+ : verdict === 'NEEDS_REVIEW'
+ ? 'NEEDS REVIEW'
+ : 'BLOCKED';
+
+ const verdictColor =
+ verdict === 'READY_TO_SHIP'
+ ? '#10B981'
+ : verdict === 'NEEDS_REVIEW'
+ ? '#F59E0B'
+ : '#EF4444';
+
+ const summary = report?.summary ?? {
+ filesChanged: 0,
+ linesAdded: 0,
+ linesDeleted: 0,
+ symbolsAffected: 0,
+ apiRoutesAffected: 0,
+ testsAdded: 0,
+ testsAffected: 0,
+ highRiskCount: 0,
+ mediumRiskCount: 0,
+ lowRiskCount: 0
+ };
+
+ const risks = report?.risks ?? [];
+ const impact = report?.impact;
+ const rules = report?.rules ?? [];
+ const checks = report?.checks;
+ const breakage = report?.breakageRisks ?? [];
+
+ return `
+
+
+
+
+ ProofCode Change Verification
+
+
+
+
+
+
+
+
+
Verification Score
+
+
${verdictText}
+
+
+
+
+
Files Changed
+
${summary.filesChanged}
+
+${summary.linesAdded} / -${summary.linesDeleted} lines
+
+
+
Functions Affected
+
${summary.symbolsAffected}
+
${summary.apiRoutesAffected} API routes
+
+
+
Critical Risks
+
+ ${summary.highRiskCount}
+
+
+${summary.mediumRiskCount} medium, ${summary.lowRiskCount} low
+
+
+
Untested Callers
+
+ ${impact?.untestedCallersCount ?? 0}
+
+
${impact?.totalCallersCount ?? 0} total callers
+
+
+
+
+
+
+
+
+ ${
+ risks.length === 0
+ ? `
+
✅ No Static Risks Detected
+
No SQL injection, missing authorization, hardcoded secrets, or unhandled errors found in changed files.
+
`
+ : risks
+ .map(
+ (r) => `
+
+
+
${r.description}
+
+ ${
+ r.evidenceTrace.length > 0
+ ? `
Evidence Trace:\n${r.evidenceTrace
+ .map((t, idx) => ` [Step ${idx + 1}] Line ${t.line}: ${t.description}`)
+ .join('\n')}`
+ : ''
+ }
+
+ ${
+ r.snippet
+ ? `
${escapeHtml(r.snippet)}
`
+ : ''
+ }
+
+
+ Action: ${r.recommendation}
+
+
+ `
+ )
+ .join('')
+ }
+
+
+
+
+ ${
+ breakage.length === 0
+ ? `
+
✅ No Regression Breakage Risks Detected
+
No modified core services with untested dependents, critical route vulnerabilities, or rule breaches found.
+
`
+ : breakage
+ .map(
+ (b) => `
+
+
+
Impact: ${b.detail}
+ ${
+ b.affectedCallersOrRoutes.length > 0
+ ? `
+ Dependents at risk: ${b.affectedCallersOrRoutes
+ .map(
+ (c) =>
+ `${c}`
+ )
+ .join(' ')}
+
`
+ : ''
+ }
+
+ `
+ )
+ .join('')
+ }
+
+
+
+
+ ${
+ !impact || impact.changedSymbols.length === 0
+ ? `⚡ No exported symbols were modified.
`
+ : `
+
+
+
+
+
+ ${impact.changedSymbols
+ .map((sym) => {
+ const untestedCallers = sym.callers.filter((c) => !c.hasTest);
+ return `
+
+
+
+
+ ${
+ sym.callers.length === 0
+ ? `
+ No downstream callers detected in repository (leaf module or top-level entrypoint).
+
`
+ : `
+
+ ${sym.callers
+ .map(
+ (c) => `
+
+
+ ↳
+ ${escapeHtml(c.callerName)}
+ ${escapeHtml(path.basename(c.filePath))}:${c.line}
+ ${
+ c.hasTest
+ ? `✓ Test Covered`
+ : `⚠️ Untested Caller`
+ }
+
+
+
+ `
+ )
+ .join('')}
+
+ `
+ }
+
+ `;
+ })
+ .join('')}
+
+
+
+ ${
+ impact.affectedRoutes.length > 0
+ ? `
+
+
+ 🌐 Directly Affected API Endpoints (${impact.affectedRoutes.length})
+
+ ${impact.affectedRoutes
+ .map(
+ (r) => `
+
+
+ ${escapeHtml(r.method)}
+ ${escapeHtml(r.routePath)}
+ (${escapeHtml(path.basename(r.filePath))}:${r.line})
+
+
+
+ `
+ )
+ .join('')}
+
+ `
+ : ''
+ }
+ `
+ }
+
+
+
+
+
+
+
+
+ | Status |
+ Project Rule |
+ Evidence / Details |
+ Action |
+
+
+
+ ${
+ rules.length === 0
+ ? '| No rules defined. Click "Edit Rules" to create .proofcode/rules.md. |
'
+ : rules
+ .map(
+ (r) => `
+
+ |
+
+ ${r.status}
+
+ |
+
+ ${r.rule.title}
+ ${r.rule.description}
+ |
+
+ ${r.detectedMessage || 'Compliant with policy.'}
+ |
+
+ ${
+ r.file && r.line
+ ? ``
+ : '—'
+ }
+ |
+
+ `
+ )
+ .join('')
+ }
+
+
+
+
+
+
+
+
+
+
+
+ | Check Name |
+ Status |
+ Details |
+
+
+
+ ${
+ checks
+ ? `
+
+ | ${checks.typescript.name} |
+ ${checks.typescript.status} |
+ ${checks.typescript.message || 'Pass'} |
+
+
+ | ${checks.eslint.name} |
+ ${checks.eslint.status} |
+ ${checks.eslint.message || 'Pass'} |
+
+
+ | ${checks.unitTests.name} |
+ ${checks.unitTests.status} |
+ ${checks.unitTests.message || 'Pass'} |
+
+
+ | ${checks.build.name} |
+ ${checks.build.status} |
+ ${checks.build.message || 'Pass'} |
+
+ `
+ : '| No live checks configured yet. |
'
+ }
+
+
+
+
+
+
+
+`;
+ }
+}
+
+function escapeHtml(text: string): string {
+ return text
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
diff --git a/tests/__mocks__/vscode.ts b/tests/__mocks__/vscode.ts
new file mode 100644
index 0000000..73aca79
--- /dev/null
+++ b/tests/__mocks__/vscode.ts
@@ -0,0 +1,148 @@
+export class TreeItem {
+ constructor(public label: string, public collapsibleState?: any) {}
+}
+
+export enum TreeItemCollapsibleState {
+ None = 0,
+ Collapsed = 1,
+ Expanded = 2
+}
+
+export class ThemeIcon {
+ constructor(public id: string) {}
+}
+
+export class EventEmitter {
+ public event = (listener: (e: T) => any) => ({ dispose: () => {} });
+ public fire(data?: T) {}
+}
+
+export class Position {
+ constructor(public line: number, public character: number) {}
+}
+
+export class Range {
+ public start: Position;
+ public end: Position;
+ constructor(
+ startLineOrPos: number | Position,
+ startCharOrEndPos: number | Position,
+ endLine?: number,
+ endCharacter?: number
+ ) {
+ if (typeof startLineOrPos === 'number') {
+ this.start = new Position(startLineOrPos, startCharOrEndPos as number);
+ this.end = new Position(endLine ?? startLineOrPos, endCharacter ?? (startCharOrEndPos as number));
+ } else {
+ this.start = startLineOrPos;
+ this.end = startCharOrEndPos as Position;
+ }
+ }
+}
+
+export class Selection {
+ constructor(public anchor: Position, public active: Position) {}
+}
+
+export class CodeLens {
+ constructor(public range: Range, public command?: { title: string; command: string; tooltip?: string }) {}
+}
+
+export class CodeActionKind {
+ public static readonly QuickFix = new CodeActionKind('quickfix');
+ constructor(public readonly value: string) {}
+}
+
+export class WorkspaceEdit {
+ public replace(_uri: any, _range: Range, _newText: string) {}
+ public insert(_uri: any, _position: Position, _newText: string) {}
+}
+
+export class CodeAction {
+ public edit?: WorkspaceEdit;
+ public isPreferred?: boolean;
+ public diagnostics?: Diagnostic[];
+ constructor(public title: string, public kind?: CodeActionKind) {}
+}
+
+export class Diagnostic {
+ public source?: string;
+ public code?: any;
+ constructor(public range: Range, public message: string, public severity: any) {}
+}
+
+export enum DiagnosticSeverity {
+ Error = 0,
+ Warning = 1,
+ Information = 2,
+ Hint = 3
+}
+
+export const Uri = {
+ file: (path: string) => ({ fsPath: path, path }),
+ joinPath: (base: any, ...segments: string[]) => ({
+ fsPath: `${base.fsPath}/${segments.join('/')}`
+ })
+};
+
+export const window = {
+ createStatusBarItem: () => ({
+ text: '',
+ tooltip: '',
+ command: '',
+ show: () => {},
+ dispose: () => {}
+ }),
+ registerTreeDataProvider: () => ({ dispose: () => {} }),
+ showInformationMessage: async () => {},
+ showWarningMessage: async () => {},
+ showErrorMessage: async () => {},
+ withProgress: async (_opts: any, task: any) => task(),
+ createWebviewPanel: () => ({
+ webview: {
+ html: '',
+ onDidReceiveMessage: () => ({ dispose: () => {} }),
+ asWebviewUri: (uri: any) => uri,
+ cspSource: 'https:'
+ },
+ onDidDispose: () => ({ dispose: () => {} }),
+ reveal: () => {},
+ dispose: () => {}
+ })
+};
+
+export const workspace = {
+ workspaceFolders: [],
+ getConfiguration: () => ({
+ get: (key: string, defaultValue: any) => defaultValue
+ }),
+ openTextDocument: async () => ({}),
+ onDidSaveTextDocument: () => ({ dispose: () => {} })
+};
+
+export const languages = {
+ createDiagnosticCollection: (name?: string) => ({
+ name,
+ clear: () => {},
+ set: (_uri: any, _diags: any[]) => {},
+ delete: () => {},
+ dispose: () => {}
+ }),
+ registerCodeLensProvider: () => ({ dispose: () => {} }),
+ registerCodeActionsProvider: () => ({ dispose: () => {} })
+};
+
+export const commands = {
+ registerCommand: (_cmd: string, _cb: any) => ({ dispose: () => {} }),
+ executeCommand: async () => {}
+};
+
+export enum StatusBarAlignment {
+ Left = 1,
+ Right = 2
+}
+
+export enum ViewColumn {
+ One = 1,
+ Two = 2
+}
diff --git a/tests/astAnalyzer.test.ts b/tests/astAnalyzer.test.ts
new file mode 100644
index 0000000..856335f
--- /dev/null
+++ b/tests/astAnalyzer.test.ts
@@ -0,0 +1,64 @@
+import { describe, expect, it } from 'vitest';
+import { AstAnalyzer } from '../src/analyzer/astAnalyzer';
+
+describe('AstAnalyzer', () => {
+ it('should extract exported functions and their calls', () => {
+ const code = `
+import { db } from './db';
+
+export function getUser(userId: string) {
+ const user = db.user.findUnique(userId);
+ return user;
+}
+
+export const updateUser = (userId: string, data: any) => {
+ db.user.update(userId, data);
+};
+`;
+
+ const analysis = AstAnalyzer.analyzeFile('/test/auth.ts', code);
+ expect(analysis.symbols).toHaveLength(2);
+ expect(analysis.symbols[0].name).toBe('getUser');
+ expect(analysis.symbols[0].isExported).toBe(true);
+ expect(analysis.symbols[0].calls).toContain('findUnique');
+
+ expect(analysis.symbols[1].name).toBe('updateUser');
+ expect(analysis.symbols[1].kind).toBe('arrow_function');
+ expect(analysis.symbols[1].isExported).toBe(true);
+ });
+
+ it('should detect Next.js App Router API endpoints', () => {
+ const code = `
+export async function GET(request: Request) {
+ return Response.json({ ok: true });
+}
+
+export async function POST(request: Request) {
+ return Response.json({ created: true });
+}
+`;
+
+ const analysis = AstAnalyzer.analyzeFile('/app/api/orders/route.ts', code);
+ expect(analysis.hasApiRoute).toBe(true);
+ expect(analysis.apiRoutes).toHaveLength(2);
+ expect(analysis.apiRoutes[0].method).toBe('GET');
+ expect(analysis.apiRoutes[1].method).toBe('POST');
+ });
+
+ it('should find symbols intersecting with modified lines', () => {
+ const code = `
+export function first() {
+ return 1;
+}
+
+export function second() {
+ return 2;
+}
+`;
+ const analysis = AstAnalyzer.analyzeFile('/test/funcs.ts', code);
+ // line 2 is in 'first'
+ const touched = AstAnalyzer.findSymbolsAtLines(analysis.symbols, [3]);
+ expect(touched).toHaveLength(1);
+ expect(touched[0].name).toBe('first');
+ });
+});
diff --git a/tests/codeActionProvider.test.ts b/tests/codeActionProvider.test.ts
new file mode 100644
index 0000000..c8c6d8e
--- /dev/null
+++ b/tests/codeActionProvider.test.ts
@@ -0,0 +1,101 @@
+import { describe, expect, it } from 'vitest';
+import * as vscode from 'vscode';
+import { ProofCodeCodeActionProvider } from '../src/ui/codeActionProvider';
+
+describe('ProofCodeCodeActionProvider', () => {
+ const mockDocument: any = {
+ uri: vscode.Uri.file('/src/auth.ts'),
+ lineAt: (line: number) => ({
+ text: ' const user = await db.user.findUnique({ where: { id } });',
+ range: new vscode.Range(line, 0, line, 60)
+ })
+ };
+
+ it('should return empty actions when no ProofCode diagnostics are present', () => {
+ const provider = new ProofCodeCodeActionProvider();
+ const actions = provider.provideCodeActions(
+ mockDocument,
+ new vscode.Range(0, 0, 0, 0),
+ {
+ diagnostics: [
+ new vscode.Diagnostic(
+ new vscode.Range(0, 0, 0, 0),
+ 'Some external linter error',
+ vscode.DiagnosticSeverity.Error
+ )
+ ]
+ } as any,
+ {} as any
+ );
+
+ expect(actions).toEqual([]);
+ });
+
+ it('should provide QuickFix to wrap unhandled async operation in try/catch (PC-REL-006)', () => {
+ const provider = new ProofCodeCodeActionProvider();
+ const diag = new vscode.Diagnostic(
+ new vscode.Range(1, 4, 1, 30),
+ '[PC-REL-006] Unhandled Asynchronous Operation',
+ vscode.DiagnosticSeverity.Information
+ );
+ diag.source = 'ProofCode';
+ diag.code = 'PC-REL-006';
+
+ const actions = provider.provideCodeActions(
+ mockDocument,
+ new vscode.Range(1, 0, 1, 0),
+ { diagnostics: [diag] } as any,
+ {} as any
+ );
+
+ expect(actions.length).toBe(1);
+ expect(actions[0].title).toContain('Wrap in try/catch block');
+ expect(actions[0].edit).toBeDefined();
+ expect(actions[0].isPreferred).toBe(true);
+ });
+
+ it('should provide QuickFixes for unhandled null database result (PC-DATA-001)', () => {
+ const provider = new ProofCodeCodeActionProvider();
+ const diag = new vscode.Diagnostic(
+ new vscode.Range(1, 4, 1, 30),
+ '[PC-DATA-001] Unhandled Null/Undefined Database Result (user)',
+ vscode.DiagnosticSeverity.Warning
+ );
+ diag.source = 'ProofCode';
+ diag.code = 'PC-DATA-001';
+
+ const actions = provider.provideCodeActions(
+ mockDocument,
+ new vscode.Range(1, 0, 1, 0),
+ { diagnostics: [diag] } as any,
+ {} as any
+ );
+
+ expect(actions.length).toBeGreaterThanOrEqual(1);
+ const guardAction = actions.find((a) => a.title.includes('null guard'));
+ expect(guardAction).toBeDefined();
+ expect(guardAction?.title).toContain('if (!user)');
+ });
+
+ it('should provide QuickFix for missing authorization in API route (PC-AUTH-001)', () => {
+ const provider = new ProofCodeCodeActionProvider();
+ const diag = new vscode.Diagnostic(
+ new vscode.Range(0, 0, 0, 20),
+ '[PC-AUTH-001] Missing Authorization Check in API Route (GET)',
+ vscode.DiagnosticSeverity.Error
+ );
+ diag.source = 'ProofCode';
+ diag.code = 'PC-AUTH-001';
+
+ const actions = provider.provideCodeActions(
+ mockDocument,
+ new vscode.Range(0, 0, 0, 0),
+ { diagnostics: [diag] } as any,
+ {} as any
+ );
+
+ expect(actions.length).toBe(1);
+ expect(actions[0].title).toContain('session authentication guard');
+ expect(actions[0].edit).toBeDefined();
+ });
+});
diff --git a/tests/codeLensProvider.test.ts b/tests/codeLensProvider.test.ts
new file mode 100644
index 0000000..74d56ff
--- /dev/null
+++ b/tests/codeLensProvider.test.ts
@@ -0,0 +1,88 @@
+import { describe, expect, it } from 'vitest';
+import * as vscode from 'vscode';
+import { ProofCodeCodeLensProvider } from '../src/ui/codeLensProvider';
+import { VerificationReport } from '../src/types';
+
+describe('ProofCodeCodeLensProvider', () => {
+ it('should return empty array when no report is set', () => {
+ const provider = new ProofCodeCodeLensProvider();
+ const doc: any = {
+ fileName: '/workspace/src/auth.ts',
+ lineCount: 10,
+ lineAt: () => ({ text: 'export function getUser() {}' })
+ };
+
+ const lenses = provider.provideCodeLenses(doc, {} as any);
+ expect(lenses).toEqual([]);
+ });
+
+ it('should provide CodeLenses for changed functions with caller info and risks', () => {
+ const provider = new ProofCodeCodeLensProvider();
+ const mockReport: Partial = {
+ summary: {
+ filesChanged: 1,
+ linesAdded: 10,
+ linesDeleted: 2,
+ symbolsAffected: 1,
+ apiRoutesAffected: 0,
+ testsAdded: 0,
+ testsAffected: 0,
+ highRiskCount: 1,
+ mediumRiskCount: 0,
+ lowRiskCount: 0
+ },
+ impact: {
+ changedFiles: ['/workspace/src/auth.ts'],
+ changedSymbols: [
+ {
+ symbolName: 'getUser',
+ kind: 'function',
+ filePath: '/workspace/src/auth.ts',
+ callers: [
+ { callerName: 'ProfilePage', filePath: '/src/Profile.tsx', line: 10, hasTest: false },
+ { callerName: 'Dashboard', filePath: '/src/Dashboard.tsx', line: 12, hasTest: true }
+ ],
+ isApiRoute: false
+ }
+ ],
+ affectedFiles: ['/src/Profile.tsx', '/src/Dashboard.tsx'],
+ affectedCallers: [],
+ affectedRoutes: [],
+ totalCallersCount: 2,
+ untestedCallersCount: 1
+ },
+ risks: [
+ {
+ id: 'risk-1',
+ ruleId: 'PC-AUTH-001',
+ category: 'authorization',
+ title: 'Missing Auth',
+ severity: 'HIGH',
+ description: 'No auth',
+ file: '/workspace/src/auth.ts',
+ line: 3,
+ snippet: 'const user = db.user()',
+ evidenceTrace: [],
+ recommendation: 'Fix it'
+ }
+ ]
+ };
+
+ provider.setReport(mockReport as VerificationReport);
+
+ const doc: any = {
+ fileName: '/workspace/src/auth.ts',
+ lineCount: 5,
+ lineAt: (i: number) => {
+ if (i === 1) return { text: 'export function getUser() {' };
+ return { text: '}' };
+ }
+ };
+
+ const lenses = provider.provideCodeLenses(doc, {} as any);
+ expect(lenses.length).toBe(1);
+ expect(lenses[0].command?.title).toContain('2 callers (1 untested)');
+ expect(lenses[0].command?.title).toContain('1 risk');
+ expect(lenses[0].command?.command).toBe('proofcode.openDashboard');
+ });
+});
diff --git a/tests/demoWorkflow.test.ts b/tests/demoWorkflow.test.ts
new file mode 100644
index 0000000..c32eb64
--- /dev/null
+++ b/tests/demoWorkflow.test.ts
@@ -0,0 +1,41 @@
+import * as path from 'node:path';
+import { describe, expect, it } from 'vitest';
+import { ProofCodeEngine } from '../src/engine/proofCodeEngine';
+
+describe('Killer Workflow Demo Fixture', () => {
+ it('should analyze the demo app and detect all high risks, impact, and rule violations', async () => {
+ const demoDir = path.resolve(__dirname, '../examples/demo-app');
+ const engine = new ProofCodeEngine(demoDir);
+
+ const report = await engine.verify();
+
+ expect(report).toBeDefined();
+ expect(report.verdict).toBe('BLOCKED');
+ expect(report.summary.highRiskCount).toBeGreaterThanOrEqual(1);
+
+ // Verify SQL injection detection
+ const sqlRisk = report.risks.find((r) => r.ruleId === 'PC-SEC-001');
+ expect(sqlRisk).toBeDefined();
+ expect(sqlRisk?.file).toContain('login/route.ts');
+
+ // Verify Missing Authorization in API route
+ const authRisk = report.risks.find(
+ (r) => r.ruleId === 'PC-AUTH-001' && r.file.includes('orders')
+ );
+ expect(authRisk).toBeDefined();
+
+ // Verify Rule Violations
+ const passwordHashViolation = report.rules.find((r) =>
+ r.rule.description.includes('passwordHash') && r.status === 'VIOLATION'
+ );
+ expect(passwordHashViolation).toBeDefined();
+
+ // Verify Impact Analysis
+ const changedUserSymbol = report.impact.changedSymbols.find(
+ (s) => s.symbolName === 'getUser'
+ );
+ expect(changedUserSymbol).toBeDefined();
+ expect(changedUserSymbol?.callers.length).toBeGreaterThanOrEqual(3);
+ expect(report.impact.untestedCallersCount).toBeGreaterThan(0);
+ });
+});
diff --git a/tests/diffParser.test.ts b/tests/diffParser.test.ts
new file mode 100644
index 0000000..8a69cda
--- /dev/null
+++ b/tests/diffParser.test.ts
@@ -0,0 +1,55 @@
+import { describe, expect, it } from 'vitest';
+import { DiffParser } from '../src/git/diffParser';
+
+describe('DiffParser', () => {
+ it('should parse an empty diff without error', () => {
+ const result = DiffParser.parse('', '/test/root');
+ expect(result.files).toHaveLength(0);
+ expect(result.totalFilesChanged).toBe(0);
+ expect(result.totalInsertions).toBe(0);
+ expect(result.totalDeletions).toBe(0);
+ });
+
+ it('should parse a standard unified diff with additions and deletions', () => {
+ const sampleDiff = `diff --git a/src/user.ts b/src/user.ts
+index e69de29..49e29a1 100644
+--- a/src/user.ts
++++ b/src/user.ts
+@@ -10,3 +10,5 @@ export function getUser(id: string) {
+- return db.user.find(id);
++ const user = await db.user.findUnique({ where: { id } });
++ return user;
+ }
++export function deleteUser() {}
+`;
+
+ const result = DiffParser.parse(sampleDiff, '/workspace');
+ expect(result.files).toHaveLength(1);
+ const file = result.files[0];
+ expect(file.newPath).toBe('src/user.ts');
+ expect(file.addedLines).toEqual([10, 11, 13]);
+ expect(file.deletedLines).toEqual([10]);
+ expect(file.modifiedLineRanges).toEqual([
+ { start: 10, end: 11 },
+ { start: 13, end: 13 }
+ ]);
+ });
+
+ it('should identify new files correctly', () => {
+ const newFileDiff = `diff --git a/src/new-route.ts b/src/new-route.ts
+new file mode 100644
+index 0000000..49e29a1
+--- /dev/null
++++ b/src/new-route.ts
+@@ -0,0 +1,3 @@
++export async function GET() {
++ return Response.json({ status: 'ok' });
++}
+`;
+
+ const result = DiffParser.parse(newFileDiff, '/workspace');
+ expect(result.files).toHaveLength(1);
+ expect(result.files[0].isNew).toBe(true);
+ expect(result.files[0].addedLines).toEqual([1, 2, 3]);
+ });
+});
diff --git a/tests/extension.test.ts b/tests/extension.test.ts
new file mode 100644
index 0000000..5f0f7b6
--- /dev/null
+++ b/tests/extension.test.ts
@@ -0,0 +1,33 @@
+import { describe, expect, it } from 'vitest';
+import * as vscode from 'vscode';
+import { activate, deactivate } from '../src/extension/index';
+
+describe('Extension Lifecycle', () => {
+ it('should activate extension and register commands, views, and subscriptions', () => {
+ const registeredCommands: string[] = [];
+ (vscode.commands.registerCommand as any) = (cmd: string, _callback: any) => {
+ registeredCommands.push(cmd);
+ return { dispose: () => {} };
+ };
+
+ const mockContext: any = {
+ subscriptions: [],
+ extensionUri: vscode.Uri.file('/extension')
+ };
+
+ expect(() => activate(mockContext)).not.toThrow();
+
+ // Verify key commands registered
+ expect(registeredCommands).toContain('proofcode.verifyChange');
+ expect(registeredCommands).toContain('proofcode.openDashboard');
+ expect(registeredCommands).toContain('proofcode.initRules');
+ expect(registeredCommands).toContain('proofcode.openEvidence');
+ expect(registeredCommands).toContain('proofcode.exportReport');
+
+ // Verify subscriptions registered
+ expect(mockContext.subscriptions.length).toBeGreaterThan(0);
+
+ // Verify deactivate does not throw
+ expect(() => deactivate()).not.toThrow();
+ });
+});
diff --git a/tests/projectGraph.test.ts b/tests/projectGraph.test.ts
new file mode 100644
index 0000000..af5ffa8
--- /dev/null
+++ b/tests/projectGraph.test.ts
@@ -0,0 +1,56 @@
+import * as fs from 'node:fs';
+import * as os from 'node:os';
+import * as path from 'node:path';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
+import { ProjectGraph } from '../src/graph/projectGraph';
+
+describe('ProjectGraph', () => {
+ let tempDir: string;
+
+ beforeEach(() => {
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'proofcode-graph-test-'));
+ });
+
+ afterEach(() => {
+ fs.rmSync(tempDir, { recursive: true, force: true });
+ });
+
+ it('should map imports and compute reverse dependents', async () => {
+ // File A: userService.ts
+ const userPath = path.join(tempDir, 'userService.ts');
+ fs.writeFileSync(
+ userPath,
+ `export function getUser(id: string) { return { id }; }`,
+ 'utf8'
+ );
+
+ // File B: orderController.ts imports userService
+ const orderPath = path.join(tempDir, 'orderController.ts');
+ fs.writeFileSync(
+ orderPath,
+ `import { getUser } from './userService';
+export function handleOrder(userId: string) {
+ const user = getUser(userId);
+ return user;
+}`,
+ 'utf8'
+ );
+
+ const graph = new ProjectGraph(tempDir);
+ await graph.buildGraph([userPath, orderPath]);
+
+ const dependents = graph.getDependents(userPath);
+ expect(dependents.has(orderPath)).toBe(true);
+
+ const changedLinesMap = new Map();
+ changedLinesMap.set(userPath, [1]);
+
+ const impact = graph.computeImpact(changedLinesMap);
+ expect(impact.changedFiles).toContain(userPath);
+ expect(impact.changedSymbols).toHaveLength(1);
+ expect(impact.changedSymbols[0].symbolName).toBe('getUser');
+ expect(impact.changedSymbols[0].callers).toHaveLength(1);
+ expect(impact.changedSymbols[0].callers[0].callerName).toBe('handleOrder');
+ expect(impact.untestedCallersCount).toBe(1);
+ });
+});
diff --git a/tests/projectRulesEngine.test.ts b/tests/projectRulesEngine.test.ts
new file mode 100644
index 0000000..aba7745
--- /dev/null
+++ b/tests/projectRulesEngine.test.ts
@@ -0,0 +1,41 @@
+import { describe, expect, it } from 'vitest';
+import { ProjectGraph } from '../src/graph/projectGraph';
+import { ProjectRulesEngine } from '../src/rules/projectRulesEngine';
+
+describe('ProjectRulesEngine', () => {
+ it('should parse default rules when no file exists', () => {
+ const engine = new ProjectRulesEngine('/nonexistent/workspace');
+ const rules = engine.parseRules();
+ expect(rules.length).toBeGreaterThanOrEqual(4);
+ expect(rules.some((r) => r.title.toLowerCase().includes('authentication'))).toBe(true);
+ });
+
+ it('should detect passwordHash leakage in response objects', async () => {
+ const engine = new ProjectRulesEngine(process.cwd());
+ const graph = new ProjectGraph(process.cwd());
+
+ const fakeDiff = [
+ {
+ oldPath: 'src/api/user.ts',
+ newPath: 'src/api/user.ts',
+ isNew: false,
+ isDeleted: false,
+ isRenamed: false,
+ hunks: [],
+ addedLines: [1],
+ deletedLines: [],
+ modifiedLineRanges: [{ start: 1, end: 1 }]
+ }
+ ];
+
+ // Evaluate against rule directly
+ const rule = {
+ id: 'RULE-2',
+ title: 'Sensitive Field Protection',
+ description: 'Never expose passwordHash or secret fields in responses.',
+ severity: 'HIGH' as const
+ };
+
+ expect(rule.description).toContain('passwordHash');
+ });
+});
diff --git a/tests/proofCodeEngine.test.ts b/tests/proofCodeEngine.test.ts
new file mode 100644
index 0000000..dade6d9
--- /dev/null
+++ b/tests/proofCodeEngine.test.ts
@@ -0,0 +1,137 @@
+import { describe, expect, it } from 'vitest';
+import { ProofCodeEngine } from '../src/engine/proofCodeEngine';
+import { ReportFormatter } from '../src/engine/reportFormatter';
+import { VerificationReport } from '../src/types';
+
+describe('ProofCodeEngine & ReportFormatter', () => {
+ it('should run verification on current project without crashing', async () => {
+ const engine = new ProofCodeEngine(process.cwd());
+ const report = await engine.verify();
+
+ expect(report).toBeDefined();
+ expect(report.score.total).toBeGreaterThanOrEqual(0);
+ expect(report.score.total).toBeLessThanOrEqual(100);
+ expect(['READY_TO_SHIP', 'NEEDS_REVIEW', 'BLOCKED']).toContain(report.verdict);
+ expect(report.summary).toBeDefined();
+ });
+
+ it('should format report to clean markdown', () => {
+ const sampleReport: VerificationReport = {
+ timestamp: new Date().toISOString(),
+ repoRoot: '/workspace',
+ branch: 'main',
+ score: {
+ total: 82,
+ deductions: [{ reason: '1 Medium Risk detected', amount: 7 }]
+ },
+ verdict: 'NEEDS_REVIEW',
+ summary: {
+ filesChanged: 2,
+ linesAdded: 45,
+ linesDeleted: 12,
+ symbolsAffected: 5,
+ apiRoutesAffected: 1,
+ testsAdded: 1,
+ testsAffected: 2,
+ highRiskCount: 0,
+ mediumRiskCount: 1,
+ lowRiskCount: 0
+ },
+ impact: {
+ changedFiles: ['src/user.ts'],
+ changedSymbols: [
+ {
+ symbolName: 'getUser',
+ kind: 'function',
+ filePath: 'src/user.ts',
+ callers: [
+ {
+ callerName: 'renderDashboard',
+ filePath: 'src/dashboard.ts',
+ line: 25,
+ hasTest: false
+ }
+ ],
+ isApiRoute: false
+ }
+ ],
+ affectedFiles: ['src/dashboard.ts'],
+ affectedCallers: [],
+ affectedRoutes: [],
+ totalCallersCount: 1,
+ untestedCallersCount: 1
+ },
+ risks: [
+ {
+ id: 'RISK-1',
+ ruleId: 'PC-SEC-005',
+ category: 'security',
+ title: 'Unsafe User-Controlled URL',
+ severity: 'MEDIUM',
+ description: 'Parameter passed to fetch without validation',
+ file: 'src/proxy.ts',
+ line: 14,
+ snippet: 'fetch(req.body.url)',
+ evidenceTrace: [
+ {
+ file: 'src/proxy.ts',
+ line: 14,
+ description: 'fetch called with req.body.url'
+ }
+ ],
+ recommendation: 'Validate url parameter'
+ }
+ ],
+ rules: [
+ {
+ rule: {
+ id: 'RULE-1',
+ title: 'API Authentication',
+ description: 'Authenticate all routes',
+ severity: 'HIGH'
+ },
+ status: 'PASS'
+ }
+ ],
+ checks: {
+ typescript: { name: 'TypeScript', status: 'PASS' },
+ eslint: { name: 'ESLint', status: 'PASS' },
+ unitTests: { name: 'Unit Tests', status: 'PASS' },
+ build: { name: 'Build Check', status: 'PASS' }
+ }
+ };
+
+ const md = ReportFormatter.toMarkdown(sampleReport);
+ expect(md).toContain('# 🛡️ ProofCode Verification Report');
+ expect(md).toContain('82%');
+ expect(md).toContain('NEEDS REVIEW');
+ expect(md).toContain('Unsafe User-Controlled URL');
+ expect(md).toContain('getUser');
+
+ const terminal = ReportFormatter.toTerminal(sampleReport);
+ expect(terminal).toContain('PROOFCODE CHANGE VERIFICATION');
+ expect(terminal).toContain('82%');
+
+ const json = ReportFormatter.toJson(sampleReport);
+ expect(JSON.parse(json).score.total).toBe(82);
+
+ sampleReport.breakageRisks = [
+ {
+ id: 'BRK-1',
+ severity: 'HIGH',
+ area: 'Authentication & Session Flow',
+ trigger: 'Symbol auth was modified',
+ detail: '3 downstream callers depend on this behavior.',
+ affectedCallersOrRoutes: ['login (route.ts)', 'AdminPanel.tsx']
+ }
+ ];
+
+ const mdWithBreakage = ReportFormatter.toMarkdown(sampleReport);
+ expect(mdWithBreakage).toContain('## 💥 What Could This Change Break?');
+ expect(mdWithBreakage).toContain('Authentication & Session Flow');
+
+ const termWithBreakage = ReportFormatter.toTerminal(sampleReport);
+ expect(termWithBreakage).toContain('WHAT COULD THIS CHANGE BREAK?');
+ expect(termWithBreakage).toContain('Authentication & Session Flow');
+ });
+});
diff --git a/tests/riskEngine.test.ts b/tests/riskEngine.test.ts
new file mode 100644
index 0000000..1c249a4
--- /dev/null
+++ b/tests/riskEngine.test.ts
@@ -0,0 +1,134 @@
+import { describe, expect, it } from 'vitest';
+import { RiskEngine } from '../src/rules/riskEngine';
+
+describe('RiskEngine', () => {
+ it('should detect SQL injection via template literals with evidence', () => {
+ const code = `
+export async function queryUser(id: string) {
+ const result = await db.query(\`SELECT * FROM users WHERE id = '\${id}'\`);
+ return result;
+}
+`;
+ const findings = RiskEngine.analyzeFile('/src/db.ts', code);
+ const sqlRisk = findings.find((f) => f.ruleId === 'PC-SEC-001');
+ expect(sqlRisk).toBeDefined();
+ expect(sqlRisk?.severity).toBe('HIGH');
+ expect(sqlRisk?.evidenceTrace.length).toBeGreaterThan(0);
+ expect(sqlRisk?.evidenceTrace[0].description).toContain('template literal');
+ });
+
+ it('should detect SQL injection via string concatenation', () => {
+ const code = `
+export async function findOrders(status: string) {
+ return pool.query("SELECT * FROM orders WHERE status = '" + status + "'");
+}
+`;
+ const findings = RiskEngine.analyzeFile('/src/orders.ts', code);
+ const sqlRisk = findings.find((f) => f.ruleId === 'PC-SEC-001');
+ expect(sqlRisk).toBeDefined();
+ expect(sqlRisk?.title).toContain('String Concatenation');
+ });
+
+ it('should detect hardcoded secrets (AWS and OpenAI keys)', () => {
+ const code = `
+const awsKey = "AKIAIOSFODNN7EXAMPLE";
+const openaiKey = "sk-1234567890abcdef1234567890abcdef";
+`;
+ const findings = RiskEngine.analyzeFile('/src/config.ts', code);
+ expect(findings.some((f) => f.title.includes('AWS Access Key'))).toBe(true);
+ expect(findings.some((f) => f.title.includes('OpenAI API Secret Key'))).toBe(true);
+ });
+
+ it('should detect dangerous eval and child_process exec', () => {
+ const code = `
+export function runScript(cmd: string) {
+ return eval(cmd);
+}
+`;
+ const findings = RiskEngine.analyzeFile('/src/exec.ts', code);
+ const evalRisk = findings.find((f) => f.ruleId === 'PC-SEC-004');
+ expect(evalRisk).toBeDefined();
+ expect(evalRisk?.severity).toBe('HIGH');
+ });
+
+ it('should detect SSRF / unvalidated external requests', () => {
+ const code = `
+export async function proxy(req: any) {
+ return fetch(req.body.targetUrl);
+}
+`;
+ const findings = RiskEngine.analyzeFile('/src/proxy.ts', code);
+ const ssrfRisk = findings.find((f) => f.ruleId === 'PC-SEC-005');
+ expect(ssrfRisk).toBeDefined();
+ expect(ssrfRisk?.severity).toBe('MEDIUM');
+ });
+
+ it('should detect missing authorization in API routes accessing DB', () => {
+ const code = `
+export async function GET(req: Request) {
+ const orders = await db.order.findMany();
+ return Response.json(orders);
+}
+`;
+ const findings = RiskEngine.analyzeFile('/app/api/orders/route.ts', code);
+ const authRisk = findings.find((f) => f.ruleId === 'PC-AUTH-001');
+ expect(authRisk).toBeDefined();
+ expect(authRisk?.severity).toBe('HIGH');
+ expect(authRisk?.title).toContain('Missing Authorization Check');
+ });
+
+ it('should NOT flag API routes that check authentication', () => {
+ const code = `
+import { auth } from '@/auth';
+
+export async function GET(req: Request) {
+ const session = await auth();
+ if (!session) return new Response('Unauthorized', { status: 401 });
+ const orders = await db.order.findMany({ where: { userId: session.user.id } });
+ return Response.json(orders);
+}
+`;
+ const findings = RiskEngine.analyzeFile('/app/api/orders/route.ts', code);
+ const authRisk = findings.find((f) => f.ruleId === 'PC-AUTH-001');
+ expect(authRisk).toBeUndefined();
+ });
+
+ it('should detect unhandled null database results', () => {
+ const code = `
+export async function getProfile(id: string) {
+ const user = await db.user.findUnique({ where: { id } });
+ return user.email;
+}
+`;
+ const findings = RiskEngine.analyzeFile('/src/profile.ts', code);
+ const nullRisk = findings.find((f) => f.ruleId === 'PC-DATA-001');
+ expect(nullRisk).toBeDefined();
+ expect(nullRisk?.severity).toBe('MEDIUM');
+ expect(nullRisk?.title).toContain('Unhandled Null/Undefined Database Result');
+ });
+
+ it('should NOT flag when null check is present', () => {
+ const code = `
+export async function getProfile(id: string) {
+ const user = await db.user.findUnique({ where: { id } });
+ if (!user) throw new Error('Not found');
+ return user.email;
+}
+`;
+ const findings = RiskEngine.analyzeFile('/src/profile.ts', code);
+ const nullRisk = findings.find((f) => f.ruleId === 'PC-DATA-001');
+ expect(nullRisk).toBeUndefined();
+ });
+
+ it('should NOT flag when optional chaining is used', () => {
+ const code = `
+export async function getProfile(id: string) {
+ const user = await db.user.findUnique({ where: { id } });
+ return user?.email;
+}
+`;
+ const findings = RiskEngine.analyzeFile('/src/profile.ts', code);
+ const nullRisk = findings.find((f) => f.ruleId === 'PC-DATA-001');
+ expect(nullRisk).toBeUndefined();
+ });
+});
diff --git a/tests/testRunner.test.ts b/tests/testRunner.test.ts
new file mode 100644
index 0000000..dd7538f
--- /dev/null
+++ b/tests/testRunner.test.ts
@@ -0,0 +1,40 @@
+import * as fs from 'node:fs';
+import * as os from 'node:os';
+import * as path from 'node:path';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
+import { TestRunner } from '../src/test-runner/testRunner';
+
+describe('TestRunner', () => {
+ let tempDir: string;
+
+ beforeEach(() => {
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'proofcode-runner-test-'));
+ });
+
+ afterEach(() => {
+ fs.rmSync(tempDir, { recursive: true, force: true });
+ });
+
+ it('should detect pnpm, yarn, bun, and npm based on lockfiles', () => {
+ const runner = new TestRunner();
+
+ expect(runner.detectPackageManager(tempDir)).toBe('npm');
+
+ fs.writeFileSync(path.join(tempDir, 'pnpm-lock.yaml'), '');
+ expect(runner.detectPackageManager(tempDir)).toBe('pnpm');
+
+ fs.unlinkSync(path.join(tempDir, 'pnpm-lock.yaml'));
+ fs.writeFileSync(path.join(tempDir, 'yarn.lock'), '');
+ expect(runner.detectPackageManager(tempDir)).toBe('yarn');
+
+ fs.unlinkSync(path.join(tempDir, 'yarn.lock'));
+ fs.writeFileSync(path.join(tempDir, 'bun.lockb'), '');
+ expect(runner.detectPackageManager(tempDir)).toBe('bun');
+ });
+
+ it('should skip TypeScript check if no tsconfig.json exists', async () => {
+ const runner = new TestRunner();
+ const result = await runner.runTypeScriptCheck(tempDir);
+ expect(result.status).toBe('SKIPPED');
+ });
+});
diff --git a/tests/treeDataProvider.test.ts b/tests/treeDataProvider.test.ts
new file mode 100644
index 0000000..9273ee0
--- /dev/null
+++ b/tests/treeDataProvider.test.ts
@@ -0,0 +1,67 @@
+import { describe, expect, it } from 'vitest';
+import { VerificationReport } from '../src/types';
+import { ProofCodeTreeDataProvider } from '../src/ui/treeDataProvider';
+
+describe('ProofCodeTreeDataProvider', () => {
+ it('should show run message when no report is set', async () => {
+ const provider = new ProofCodeTreeDataProvider();
+ const children = await provider.getChildren();
+ expect(children).toHaveLength(1);
+ expect(children[0].label).toContain('Run "ProofCode: Verify Current Changes"');
+ });
+
+ it('should render root categories when a report is loaded', async () => {
+ const provider = new ProofCodeTreeDataProvider();
+ const mockReport: VerificationReport = {
+ timestamp: new Date().toISOString(),
+ repoRoot: '/workspace',
+ branch: 'working',
+ score: { total: 85, deductions: [] },
+ verdict: 'READY_TO_SHIP',
+ summary: {
+ filesChanged: 3,
+ linesAdded: 50,
+ linesDeleted: 10,
+ symbolsAffected: 4,
+ apiRoutesAffected: 1,
+ testsAdded: 1,
+ testsAffected: 1,
+ highRiskCount: 0,
+ mediumRiskCount: 0,
+ lowRiskCount: 0
+ },
+ impact: {
+ changedFiles: ['/workspace/src/app.ts'],
+ changedSymbols: [],
+ affectedFiles: [],
+ affectedCallers: [],
+ affectedRoutes: [],
+ totalCallersCount: 0,
+ untestedCallersCount: 0
+ },
+ risks: [],
+ rules: [],
+ checks: {
+ typescript: { name: 'TypeScript', status: 'PASS' },
+ eslint: { name: 'ESLint', status: 'PASS' },
+ unitTests: { name: 'Unit Tests', status: 'PASS' },
+ build: { name: 'Build', status: 'PASS' }
+ }
+ };
+
+ provider.setReport(mockReport);
+ const roots = await provider.getChildren();
+ expect(roots).toHaveLength(6);
+ expect(roots[0].label).toContain('Verification Score: 85%');
+ expect(roots[1].label).toContain('Changed Files (3)');
+ expect(roots[2].label).toContain('Impact');
+ expect(roots[3].label).toContain('Risks & Evidence (0)');
+ expect(roots[4].label).toContain('Project Rules');
+ expect(roots[5].label).toContain('Checks & Tests');
+
+ // Get children of Changed Files
+ const fileChildren = await provider.getChildren(roots[1]);
+ expect(fileChildren).toHaveLength(1);
+ expect(fileChildren[0].label).toBe('app.ts');
+ });
+});
diff --git a/tests/webviewPanel.test.ts b/tests/webviewPanel.test.ts
new file mode 100644
index 0000000..19e2b5c
--- /dev/null
+++ b/tests/webviewPanel.test.ts
@@ -0,0 +1,111 @@
+import { describe, expect, it } from 'vitest';
+import * as vscode from 'vscode';
+import { ProofCodeWebviewPanel } from '../src/ui/webviewPanel';
+import { VerificationReport } from '../src/types';
+
+describe('ProofCodeWebviewPanel', () => {
+ it('should generate rich glassmorphic HTML containing the Impact Blast Graph and tabs', () => {
+ const mockReport: VerificationReport = {
+ timestamp: new Date().toISOString(),
+ repoRoot: '/workspace',
+ branch: 'feature/auth',
+ score: { total: 76, deductions: [] },
+ verdict: 'NEEDS_REVIEW',
+ summary: {
+ filesChanged: 2,
+ linesAdded: 30,
+ linesDeleted: 5,
+ symbolsAffected: 2,
+ apiRoutesAffected: 1,
+ testsAdded: 1,
+ testsAffected: 1,
+ highRiskCount: 0,
+ mediumRiskCount: 1,
+ lowRiskCount: 2
+ },
+ impact: {
+ changedFiles: ['/workspace/src/auth.ts'],
+ changedSymbols: [
+ {
+ symbolName: 'getUser',
+ kind: 'function',
+ filePath: '/workspace/src/auth.ts',
+ callers: [
+ { callerName: 'Dashboard', filePath: '/workspace/src/Dashboard.tsx', line: 12, hasTest: true },
+ { callerName: 'AdminPanel', filePath: '/workspace/src/AdminPanel.tsx', line: 8, hasTest: false }
+ ],
+ isApiRoute: false
+ }
+ ],
+ affectedFiles: ['/workspace/src/Dashboard.tsx', '/workspace/src/AdminPanel.tsx'],
+ affectedCallers: [],
+ affectedRoutes: [
+ {
+ routePath: '/api/auth/session',
+ method: 'GET',
+ filePath: '/workspace/src/api/auth.ts',
+ line: 5
+ }
+ ],
+ totalCallersCount: 2,
+ untestedCallersCount: 1
+ },
+ risks: [],
+ breakageRisks: [
+ {
+ id: 'BRK-1',
+ severity: 'HIGH',
+ area: 'Authentication',
+ trigger: 'getUser modified',
+ detail: 'Downstream callers at risk',
+ affectedCallersOrRoutes: ['AdminPanel']
+ }
+ ],
+ rules: [],
+ checks: {
+ typescript: { name: 'TypeScript', status: 'PASS' },
+ eslint: { name: 'ESLint', status: 'PASS' },
+ unitTests: { name: 'Unit Tests', status: 'PASS' },
+ build: { name: 'Build', status: 'PASS' }
+ }
+ };
+
+ // Instantiate panel via private constructor method or test _getHtmlForWebview
+ // We can call createOrShow with mock panel
+ const mockWebview: any = {
+ html: '',
+ onDidReceiveMessage: () => ({ dispose: () => {} }),
+ asWebviewUri: (uri: any) => uri,
+ cspSource: 'https:'
+ };
+
+ const mockPanel: any = {
+ webview: mockWebview,
+ onDidDispose: () => ({ dispose: () => {} }),
+ reveal: () => {},
+ dispose: () => {}
+ };
+
+ (vscode.window.createWebviewPanel as any) = () => mockPanel;
+
+ ProofCodeWebviewPanel.createOrShow(
+ vscode.Uri.file('/ext'),
+ mockReport,
+ async () => {},
+ async () => {}
+ );
+
+ const html = mockWebview.html;
+ expect(html).toContain('ProofCode Change Verification');
+ expect(html).toContain('id="impact-search"');
+ expect(html).toContain('All Symbols');
+ expect(html).toContain('⚠️ Untested Callers');
+ expect(html).toContain('getUser');
+ expect(html).toContain('Dashboard');
+ expect(html).toContain('AdminPanel');
+ expect(html).toContain('✓ Test Covered');
+ expect(html).toContain('⚠️ Untested Caller');
+ expect(html).toContain('/api/auth/session');
+ expect(html).toContain('💥 What Could Break');
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..3e4828d
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "target": "ES2022",
+ "lib": ["ES2022"],
+ "sourceMap": true,
+ "rootDir": "src",
+ "outDir": "dist",
+ "strict": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+ "noImplicitThis": true,
+ "alwaysStrict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..9a1721a
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,19 @@
+import * as path from 'node:path';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ globals: true,
+ environment: 'node',
+ include: ['src/**/*.test.ts', 'tests/**/*.test.ts'],
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html']
+ }
+ },
+ resolve: {
+ alias: {
+ vscode: path.resolve(__dirname, 'tests/__mocks__/vscode.ts')
+ }
+ }
+});