diff --git a/.github/workflows/dev-publish.yml b/.github/workflows/dev-publish.yml index e4c2d58cc..c60889728 100644 --- a/.github/workflows/dev-publish.yml +++ b/.github/workflows/dev-publish.yml @@ -40,7 +40,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 22 + node-version: 24 registry-url: https://registry.npmjs.org cache: 'npm' @@ -111,7 +111,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 22 + node-version: 24 registry-url: https://registry.npmjs.org cache: 'npm' diff --git a/.github/workflows/next-publish.yml b/.github/workflows/next-publish.yml index df1f75f6f..61a6d26f7 100644 --- a/.github/workflows/next-publish.yml +++ b/.github/workflows/next-publish.yml @@ -39,7 +39,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 22 + node-version: 24 registry-url: https://registry.npmjs.org cache: 'npm' @@ -102,7 +102,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 22 + node-version: 24 registry-url: https://registry.npmjs.org cache: 'npm' diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index 3923e8b4a..69c5e1a32 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 22 + node-version: 24 registry-url: https://registry.npmjs.org cache: 'npm' diff --git a/.node-version b/.node-version new file mode 100644 index 000000000..a45fd52cc --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24 diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..a45fd52cc --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/README.md b/README.md index c65d68b90..70dd70e9e 100644 --- a/README.md +++ b/README.md @@ -711,7 +711,7 @@ sf (CLI binary) ## Requirements -- **Node.js** ≥ 22.0.0 (24 LTS recommended) +- **Node.js** ≥ 24.0.0 (24 LTS recommended) - **An LLM provider** — any of the 20+ supported providers (see [Use Any Model](#use-any-model)) - **Git** — initialized automatically if missing diff --git a/bin/sf-from-source b/bin/sf-from-source index ec4976f9f..16ecd2087 100755 --- a/bin/sf-from-source +++ b/bin/sf-from-source @@ -16,7 +16,7 @@ # derivation degraded mode under bun). # - bun's native-addon loader doesn't inherit the system library # search path under Nix (libz.so.1 not found for forge_engine.node). -# - node 22.5+ has node:sqlite built-in; node 24 supports +# - node 24+ has node:sqlite built-in and supports # --experimental-strip-types so .ts runs directly. # - The src/resources/extensions/sf/tests/resolve-ts.mjs loader hook # already handles .js → .ts import-specifier remapping for runtime @@ -27,7 +27,7 @@ # - Exports SF_BIN_PATH=dist/loader.js so all child processes (including # subagent pi instances) use the Node.js entry point directly. # -# Requirements: node >= 22.5 on PATH (24+ recommended for strip-types), +# Requirements: node >= 24 on PATH, # node_modules populated. set -euo pipefail diff --git a/docs/README.md b/docs/README.md index 7cf5ebd67..cc0829aec 100644 --- a/docs/README.md +++ b/docs/README.md @@ -69,4 +69,3 @@ Guides for the underlying Pi SDK that SF is built on. Located in [`dev/`](./dev/ |-------|-------------| | [Building Coding Agents](./dev/building-coding-agents/README.md) | Research notes on agent design — decomposition, context engineering, cost/quality tradeoffs | | [Proposals](./dev/proposals/) | Feature proposals and workflow definitions | -| [Superpowers](./dev/superpowers/) | Plans and specs for superpower features | diff --git a/docs/dev/superpowers/plans/2026-03-17-cicd-pipeline.md b/docs/dev/superpowers/plans/2026-03-17-cicd-pipeline.md deleted file mode 100644 index b59dde61a..000000000 --- a/docs/dev/superpowers/plans/2026-03-17-cicd-pipeline.md +++ /dev/null @@ -1,1404 +0,0 @@ -# CI/CD Pipeline Implementation Plan - -> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Build a three-stage promotion pipeline (Dev → Test → Prod) with Docker images, LLM fixture replay, and npm dist-tag management. - -**Architecture:** GitHub Actions `workflow_run` trigger chains `ci.yml` success into a new `pipeline.yml` with three jobs (dev-publish, test-verify, prod-release). A `FixtureProvider` wraps `pi-ai`'s `ApiProvider` interface to record/replay LLM conversations. Two Docker images (CI builder + slim runtime) are built from a single multi-stage Dockerfile. - -**Tech Stack:** GitHub Actions, Docker (multi-stage), Node 22, Rust toolchain, npm dist-tags, GHCR - -**Spec:** `docs/superpowers/specs/2026-03-17-cicd-pipeline-design.md` - ---- - -## File Structure - -### New Files - -| File | Responsibility | -|------|---------------| -| `Dockerfile` | Multi-stage: `builder` target (CI image) + `runtime` target (user image) | -| `.github/workflows/pipeline.yml` | Three-stage promotion pipeline (Dev → Test → Prod) | -| `.github/workflows/cleanup-dev-versions.yml` | Weekly scheduled cleanup of old `-dev.` npm versions | -| `scripts/version-stamp.mjs` | Reads `package.json` version, appends `-dev.`, writes back | -| `tests/smoke/run.ts` | Smoke test runner — discovers and executes all smoke tests | -| `tests/smoke/test-version.ts` | Verify `sf --version` outputs valid semver | -| `tests/smoke/test-help.ts` | Verify `sf --help` exits 0 and contains expected output | -| `tests/smoke/test-init.ts` | Verify `sf init` creates expected files in a temp dir | -| `tests/fixtures/provider.ts` | `FixtureProvider` — wraps `ApiProvider`, records/replays turns | -| `tests/fixtures/run.ts` | Fixture test runner — loads recordings, replays via `FixtureProvider` | -| `tests/fixtures/record.ts` | Recording helper — runs a session with `SF_FIXTURE_MODE=record` | -| `tests/fixtures/recordings/agent-creates-file.json` | Sample fixture: single-turn file creation | -| `tests/fixtures/recordings/agent-reads-and-edits.json` | Fixture: multi-turn read + edit flow | -| `tests/fixtures/recordings/agent-handles-error.json` | Fixture: error response handling | -| `tests/fixtures/recordings/agent-multi-turn-tools.json` | Fixture: multi-turn tool use round-trips | -| `tests/live/run.ts` | Live LLM test runner (optional, Prod gate only) | -| `tests/live/test-anthropic-roundtrip.ts` | Real Anthropic API round-trip test | -| `tests/live/test-openai-roundtrip.ts` | Real OpenAI API round-trip test | - -### Modified Files - -| File | Change | -|------|--------| -| `package.json` | Add 6 new scripts (`test:smoke`, `test:fixtures`, etc.) | - ---- - -## Chunk 1: Version Stamp Script + Dockerfile - -### Task 1: Version Stamp Script - -**Files:** -- Create: `scripts/version-stamp.mjs` - -- [ ] **Step 1: Write the version stamp script** - -```javascript -// scripts/version-stamp.mjs -// Stamps the package.json version with -dev. for CI dev publishes. -// Usage: node scripts/version-stamp.mjs -// Example: 2.27.0 → 2.27.0-dev.a3f2c1b - -import { readFileSync, writeFileSync } from "fs"; -import { execFileSync } from "child_process"; - -const pkgPath = new URL("../package.json", import.meta.url); -const pkg = JSON.parse(readFileSync(pkgPath, "utf8")); - -const shortSha = execFileSync("git", ["rev-parse", "--short", "HEAD"], { encoding: "utf8" }).trim(); -const devVersion = `${pkg.version}-dev.${shortSha}`; - -pkg.version = devVersion; -writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); - -console.log(`Stamped version: ${devVersion}`); -``` - -- [ ] **Step 2: Test it locally** - -Run: `node scripts/version-stamp.mjs` -Expected: Outputs `Stamped version: 2.27.0-dev.` and modifies `package.json` - -- [ ] **Step 3: Revert the package.json change** - -Run: `git checkout -- package.json` - -- [ ] **Step 4: Commit** - -```bash -git add scripts/version-stamp.mjs -git commit -m "feat(ci): add version stamp script for dev publishes" -``` - ---- - -### Task 2: Multi-Stage Dockerfile - -**Files:** -- Create: `Dockerfile` - -- [ ] **Step 1: Write the Dockerfile** - -```dockerfile -# ────────────────────────────────────────────── -# Stage 1: CI Builder -# Image: ghcr.io/singularity-forge/sf-ci-builder -# Used by: pipeline.yml Dev stage -# ────────────────────────────────────────────── -FROM node:22-bookworm AS builder - -# Rust toolchain (stable, minimal profile) -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal -ENV PATH="/root/.cargo/bin:${PATH}" - -# Cross-compilation for linux-arm64 -RUN apt-get update && apt-get install -y --no-install-recommends \ - gcc-aarch64-linux-gnu \ - g++-aarch64-linux-gnu \ - && rustup target add aarch64-unknown-linux-gnu \ - && rm -rf /var/lib/apt/lists/* - -# Verify toolchain -RUN node --version && rustc --version && cargo --version - -# ────────────────────────────────────────────── -# Stage 2: Runtime -# Image: ghcr.io/singularity-forge/sf-run -# Used by: end users via docker run -# ────────────────────────────────────────────── -FROM node:22-slim AS runtime - -# Git is required for SF's git operations -RUN apt-get update && apt-get install -y --no-install-recommends \ - git \ - && rm -rf /var/lib/apt/lists/* - -# Install SF globally — version is controlled by the build arg -ARG SF_VERSION=latest -RUN npm install -g sf-run@${SF_VERSION} - -# Default working directory for user projects -WORKDIR /workspace - -ENTRYPOINT ["sf"] -CMD ["--help"] -``` - -- [ ] **Step 2: Verify builder stage builds** - -Run: `docker build --target builder -t sf-ci-builder-test .` -Expected: Completes successfully (may take 5-10 min first time) - -- [ ] **Step 3: Verify runtime stage builds** - -Run: `docker build --target runtime -t sf-run-test .` -Expected: Completes successfully - -- [ ] **Step 4: Verify runtime image works** - -Run: `docker run --rm sf-run-test --version` -Expected: Outputs a version string - -- [ ] **Step 5: Commit** - -```bash -git add Dockerfile -git commit -m "feat(ci): add multi-stage Dockerfile for CI builder and runtime images" -``` - ---- - -## Chunk 2: Smoke Tests - -### Task 3: Smoke Test Runner and Tests - -**Files:** -- Create: `tests/smoke/run.ts` -- Create: `tests/smoke/test-version.ts` -- Create: `tests/smoke/test-help.ts` -- Create: `tests/smoke/test-init.ts` -- Modify: `package.json` (add `test:smoke` script) - -- [ ] **Step 1: Create the smoke test runner** - -```typescript -// tests/smoke/run.ts -// Discovers and runs all smoke tests in this directory. -// Usage: node --experimental-strip-types tests/smoke/run.ts -// Note: Uses execFileSync (not exec) to avoid shell injection. - -import { readdirSync } from "fs"; -import { execFileSync } from "child_process"; -import { fileURLToPath } from "url"; -import { dirname, join } from "path"; - -const dir = dirname(fileURLToPath(import.meta.url)); -const tests = readdirSync(dir).filter((f) => f.startsWith("test-") && f.endsWith(".ts")); - -let passed = 0; -let failed = 0; - -for (const test of tests) { - const path = join(dir, test); - try { - execFileSync("node", ["--experimental-strip-types", path], { - encoding: "utf8", - stdio: "pipe", - timeout: 30_000, - }); - console.log(`✓ ${test}`); - passed++; - } catch (err: any) { - console.error(`✗ ${test}`); - console.error(err.stdout || ""); - console.error(err.stderr || ""); - failed++; - } -} - -console.log(`\n${passed} passed, ${failed} failed`); -if (failed > 0) process.exit(1); -``` - -- [ ] **Step 2: Create test-version.ts** - -```typescript -// tests/smoke/test-version.ts -// Verifies that `sf --version` outputs valid semver-like string. -// When SF_SMOKE_BINARY is set (CI), uses that binary directly. -// Otherwise falls back to npx sf-run. - -import { execFileSync } from "child_process"; - -const bin = process.env.SF_SMOKE_BINARY; -const output = bin - ? execFileSync(bin, ["--version"], { encoding: "utf8", timeout: 30_000 }).trim() - : execFileSync("npx", ["sf-run", "--version"], { encoding: "utf8", timeout: 30_000 }).trim(); - -if (!/^\d+\.\d+\.\d+/.test(output)) { - console.error(`Unexpected version output: "${output}"`); - process.exit(1); -} - -console.log(`version: ${output}`); -``` - -- [ ] **Step 3: Create test-help.ts** - -```typescript -// tests/smoke/test-help.ts -// Verifies that `sf --help` exits 0 and contains expected keywords. - -import { execFileSync } from "child_process"; - -const bin = process.env.SF_SMOKE_BINARY; -const output = bin - ? execFileSync(bin, ["--help"], { encoding: "utf8", timeout: 30_000 }) - : execFileSync("npx", ["sf-run", "--help"], { encoding: "utf8", timeout: 30_000 }); - -const requiredKeywords = ["sf", "usage"]; -for (const keyword of requiredKeywords) { - if (!output.toLowerCase().includes(keyword)) { - console.error(`Missing keyword "${keyword}" in help output`); - process.exit(1); - } -} - -console.log("help output OK"); -``` - -- [ ] **Step 4: Create test-init.ts** - -```typescript -// tests/smoke/test-init.ts -// Verifies that `sf init` creates expected files in a temp directory. - -import { execFileSync } from "child_process"; -import { mkdtempSync, existsSync, rmSync } from "fs"; -import { join } from "path"; -import { tmpdir } from "os"; - -const tmp = mkdtempSync(join(tmpdir(), "sf-smoke-init-")); - -try { - const bin = process.env.SF_SMOKE_BINARY; - const args = bin ? [bin, "init"] : ["npx", "sf-run", "init"]; - execFileSync(args[0], args.slice(1), { - encoding: "utf8", - cwd: tmp, - timeout: 30_000, - env: { ...process.env, SF_NON_INTERACTIVE: "1" }, - }); - - // Check that .sf directory was created - if (!existsSync(join(tmp, ".sf"))) { - console.error("Expected .sf/ directory not found after init"); - process.exit(1); - } - - console.log("init OK"); -} finally { - rmSync(tmp, { recursive: true, force: true }); -} -``` - -- [ ] **Step 5: Add test:smoke script to package.json** - -Add to `package.json` `scripts`: -```json -"test:smoke": "node --experimental-strip-types tests/smoke/run.ts" -``` - -- [ ] **Step 6: Run the smoke tests locally** - -Run: `npm run test:smoke` -Expected: All 3 tests pass (version, help, init) - -- [ ] **Step 7: Commit** - -```bash -git add tests/smoke/ package.json -git commit -m "feat(ci): add CLI smoke tests for pipeline test stage" -``` - ---- - -## Chunk 3: LLM Fixture Provider - -### Task 4: FixtureProvider Implementation - -**Files:** -- Create: `tests/fixtures/provider.ts` - -The `FixtureProvider` operates at the `ApiProvider` level defined in `packages/pi-ai/src/api-registry.ts:23-27`. The key interface is: - -```typescript -interface ApiProvider { - api: TApi; - stream: StreamFunction; - streamSimple: StreamFunction; -} -``` - -The provider is registered via `registerApiProvider()` from `packages/pi-ai/src/api-registry.ts:66`. - -- [ ] **Step 1: Write the FixtureProvider** - -```typescript -// tests/fixtures/provider.ts -// Records and replays LLM conversations at the pi-ai ApiProvider level. -// -// Record mode: wraps a real provider, saves request/response to JSON. -// Replay mode: loads saved JSON, serves responses by turn index. -// -// Controlled via environment variables: -// SF_FIXTURE_MODE=record|replay -// SF_FIXTURE_DIR=./tests/fixtures/recordings - -import { readFileSync, writeFileSync, mkdirSync } from "fs"; -import { join } from "path"; - -export interface FixtureTurn { - request: { - model: string; - messages: unknown[]; - tools?: string[]; - }; - response: { - content: unknown[]; - stopReason: string; - usage: { input: number; output: number }; - }; -} - -export interface FixtureFile { - name: string; - recorded: string; - provider: string; - model: string; - turns: FixtureTurn[]; -} - -export type FixtureMode = "record" | "replay" | "off"; - -export function getFixtureMode(): FixtureMode { - const mode = process.env.SF_FIXTURE_MODE; - if (mode === "record" || mode === "replay") return mode; - return "off"; -} - -export function getFixtureDir(): string { - return process.env.SF_FIXTURE_DIR || join(process.cwd(), "tests/fixtures/recordings"); -} - -export function loadFixture(filepath: string): FixtureFile { - const raw = readFileSync(filepath, "utf8"); - return JSON.parse(raw) as FixtureFile; -} - -export function saveFixture(filepath: string, fixture: FixtureFile): void { - const dir = filepath.substring(0, filepath.lastIndexOf("/")); - mkdirSync(dir, { recursive: true }); - writeFileSync(filepath, JSON.stringify(fixture, null, 2) + "\n"); -} - -/** - * Creates a replay-mode result from a saved fixture turn. - * Returns an object with an async result() method that resolves - * to the saved response, compatible with AssistantMessageEventStream. - */ -export function createReplayStream(turn: FixtureTurn) { - const message = { - content: turn.response.content, - stopReason: turn.response.stopReason, - usage: turn.response.usage, - }; - - return { - async *[Symbol.asyncIterator]() { - yield { type: "message_complete" as const, message }; - }, - result: async () => message, - }; -} - -/** - * FixtureRecorder collects turns during a recording session - * and saves them to a JSON file when finalized. - */ -export class FixtureRecorder { - private turns: FixtureTurn[] = []; - private name: string; - private provider: string; - private model: string; - - constructor(name: string, provider: string, model: string) { - this.name = name; - this.provider = provider; - this.model = model; - } - - addTurn(turn: FixtureTurn): void { - this.turns.push(turn); - } - - save(dir: string): string { - const fixture: FixtureFile = { - name: this.name, - recorded: new Date().toISOString(), - provider: this.provider, - model: this.model, - turns: this.turns, - }; - const filepath = join(dir, `${this.name}.json`); - saveFixture(filepath, fixture); - return filepath; - } -} - -/** - * FixtureReplayer serves saved responses by turn index. - * Throws if the conversation requests more turns than recorded. - */ -export class FixtureReplayer { - private fixture: FixtureFile; - private turnIndex = 0; - - constructor(fixture: FixtureFile) { - this.fixture = fixture; - } - - nextTurn(): FixtureTurn { - if (this.turnIndex >= this.fixture.turns.length) { - throw new Error( - `Fixture "${this.fixture.name}" exhausted: requested turn ${this.turnIndex} but only ${this.fixture.turns.length} turns recorded` - ); - } - return this.fixture.turns[this.turnIndex++]; - } - - get turnsRemaining(): number { - return this.fixture.turns.length - this.turnIndex; - } -} -``` - -Note: This provider implements the core recording/replay data structures and utilities. Wiring it into the `pi-ai` registry as a drop-in `ApiProvider` (via `registerApiProvider()` from `packages/pi-ai/src/api-registry.ts`) requires importing `@sf/pi-ai` internals, which couples tests to the build output. This integration is deferred to a follow-up task after the pipeline is operational. The current implementation validates fixture format, turn sequencing, and replay correctness independently. - -- [ ] **Step 2: Verify the file has no syntax errors** - -Run: `node --experimental-strip-types -e "import('./tests/fixtures/provider.ts').then(() => console.log('OK'))"` -Expected: `OK` - -- [ ] **Step 3: Commit** - -```bash -git add tests/fixtures/provider.ts -git commit -m "feat(ci): add FixtureProvider for LLM conversation recording and replay" -``` - ---- - -### Task 5: Fixture Test Runner - -**Files:** -- Create: `tests/fixtures/run.ts` -- Create: `tests/fixtures/recordings/agent-creates-file.json` -- Modify: `package.json` (add `test:fixtures` script) - -- [ ] **Step 1: Create a sample fixture recording** - -Save to `tests/fixtures/recordings/agent-creates-file.json`: - -```json -{ - "name": "agent-creates-file", - "recorded": "2026-03-17T00:00:00Z", - "provider": "anthropic", - "model": "claude-sonnet-4-6", - "turns": [ - { - "request": { - "model": "claude-sonnet-4-6", - "messages": [{ "role": "user", "content": "Create a file called hello.ts with a console.log" }], - "tools": ["Write", "Read"] - }, - "response": { - "content": [ - { "type": "text", "text": "I'll create hello.ts for you." }, - { - "type": "tool_use", - "id": "toolu_01", - "name": "Write", - "input": { "file_path": "hello.ts", "content": "console.log('hello');\n" } - } - ], - "stopReason": "toolUse", - "usage": { "input": 150, "output": 45 } - } - } - ] -} -``` - -- [ ] **Step 2: Create the fixture test runner** - -```typescript -// tests/fixtures/run.ts -// Loads all fixture recordings and replays them through the FixtureProvider. -// Verifies each turn produces the expected response shape. -// -// Usage: node --experimental-strip-types tests/fixtures/run.ts - -import { readdirSync } from "fs"; -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; -import { - loadFixture, - FixtureReplayer, - createReplayStream, -} from "./provider.ts"; - -const dir = dirname(fileURLToPath(import.meta.url)); -const recordingsDir = join(dir, "recordings"); - -const files = readdirSync(recordingsDir).filter((f) => f.endsWith(".json")); - -if (files.length === 0) { - console.error("No fixture recordings found in", recordingsDir); - process.exit(1); -} - -let passed = 0; -let failed = 0; - -for (const file of files) { - const filepath = join(recordingsDir, file); - try { - const fixture = loadFixture(filepath); - const replayer = new FixtureReplayer(fixture); - - // Replay each turn and verify the response is well-formed - for (let i = 0; i < fixture.turns.length; i++) { - const turn = replayer.nextTurn(); - - // Verify response has required fields - if (!turn.response.content || !Array.isArray(turn.response.content)) { - throw new Error(`Turn ${i}: response.content is not an array`); - } - if (!turn.response.stopReason) { - throw new Error(`Turn ${i}: response.stopReason is missing`); - } - if (typeof turn.response.usage?.input !== "number") { - throw new Error(`Turn ${i}: response.usage.input is not a number`); - } - - // Verify the replay stream produces a result - const stream = createReplayStream(turn); - const result = await stream.result(); - - if (!result.content) { - throw new Error(`Turn ${i}: replayed result has no content`); - } - } - - // Verify replayer is exhausted - if (replayer.turnsRemaining !== 0) { - throw new Error(`${replayer.turnsRemaining} turns remaining after replay`); - } - - console.log(`✓ ${fixture.name} (${fixture.turns.length} turns)`); - passed++; - } catch (err: any) { - console.error(`✗ ${file}: ${err.message}`); - failed++; - } -} - -console.log(`\n${passed} passed, ${failed} failed`); -if (failed > 0) process.exit(1); -``` - -- [ ] **Step 3: Add test:fixtures script to package.json** - -Add to `package.json` `scripts`: -```json -"test:fixtures": "node --experimental-strip-types tests/fixtures/run.ts" -``` - -- [ ] **Step 4: Run the fixture tests** - -Run: `npm run test:fixtures` -Expected: `✓ agent-creates-file (1 turns)` — 1 passed, 0 failed - -- [ ] **Step 5: Commit** - -```bash -git add tests/fixtures/run.ts tests/fixtures/recordings/ package.json -git commit -m "feat(ci): add fixture test runner with sample recording" -``` - ---- - -### Task 5b: Additional Fixture Recordings - -**Files:** -- Create: `tests/fixtures/recordings/agent-reads-and-edits.json` -- Create: `tests/fixtures/recordings/agent-handles-error.json` -- Create: `tests/fixtures/recordings/agent-multi-turn-tools.json` - -- [ ] **Step 1: Create multi-turn read+edit fixture** - -Save to `tests/fixtures/recordings/agent-reads-and-edits.json`: - -```json -{ - "name": "agent-reads-and-edits", - "recorded": "2026-03-17T00:00:00Z", - "provider": "anthropic", - "model": "claude-sonnet-4-6", - "turns": [ - { - "request": { - "model": "claude-sonnet-4-6", - "messages": [{ "role": "user", "content": "Read hello.ts and add a comment" }], - "tools": ["Read", "Edit"] - }, - "response": { - "content": [ - { "type": "text", "text": "Let me read the file first." }, - { "type": "tool_use", "id": "toolu_01", "name": "Read", "input": { "file_path": "hello.ts" } } - ], - "stopReason": "toolUse", - "usage": { "input": 120, "output": 35 } - } - }, - { - "request": { - "model": "claude-sonnet-4-6", - "messages": [{ "role": "tool", "content": "console.log('hello');\n" }], - "tools": ["Read", "Edit"] - }, - "response": { - "content": [ - { "type": "text", "text": "I'll add a comment." }, - { "type": "tool_use", "id": "toolu_02", "name": "Edit", "input": { "file_path": "hello.ts", "old_string": "console.log", "new_string": "// greeting\nconsole.log" } } - ], - "stopReason": "toolUse", - "usage": { "input": 180, "output": 50 } - } - } - ] -} -``` - -- [ ] **Step 2: Create error-handling fixture** - -Save to `tests/fixtures/recordings/agent-handles-error.json`: - -```json -{ - "name": "agent-handles-error", - "recorded": "2026-03-17T00:00:00Z", - "provider": "anthropic", - "model": "claude-sonnet-4-6", - "turns": [ - { - "request": { - "model": "claude-sonnet-4-6", - "messages": [{ "role": "user", "content": "Read nonexistent.ts" }], - "tools": ["Read"] - }, - "response": { - "content": [ - { "type": "text", "text": "Let me try to read that file." }, - { "type": "tool_use", "id": "toolu_01", "name": "Read", "input": { "file_path": "nonexistent.ts" } } - ], - "stopReason": "toolUse", - "usage": { "input": 100, "output": 30 } - } - }, - { - "request": { - "model": "claude-sonnet-4-6", - "messages": [{ "role": "tool", "content": "Error: File does not exist" }], - "tools": ["Read"] - }, - "response": { - "content": [ - { "type": "text", "text": "The file nonexistent.ts doesn't exist. Would you like me to create it?" } - ], - "stopReason": "stop", - "usage": { "input": 140, "output": 25 } - } - } - ] -} -``` - -- [ ] **Step 3: Create multi-turn tool use fixture** - -Save to `tests/fixtures/recordings/agent-multi-turn-tools.json`: - -```json -{ - "name": "agent-multi-turn-tools", - "recorded": "2026-03-17T00:00:00Z", - "provider": "anthropic", - "model": "claude-sonnet-4-6", - "turns": [ - { - "request": { - "model": "claude-sonnet-4-6", - "messages": [{ "role": "user", "content": "Create utils.ts with an add function, then create a test file" }], - "tools": ["Write", "Read"] - }, - "response": { - "content": [ - { "type": "text", "text": "I'll create both files." }, - { "type": "tool_use", "id": "toolu_01", "name": "Write", "input": { "file_path": "utils.ts", "content": "export function add(a: number, b: number): number {\n return a + b;\n}\n" } } - ], - "stopReason": "toolUse", - "usage": { "input": 130, "output": 55 } - } - }, - { - "request": { - "model": "claude-sonnet-4-6", - "messages": [{ "role": "tool", "content": "File created successfully" }], - "tools": ["Write", "Read"] - }, - "response": { - "content": [ - { "type": "text", "text": "Now the test file." }, - { "type": "tool_use", "id": "toolu_02", "name": "Write", "input": { "file_path": "utils.test.ts", "content": "import { add } from './utils.ts';\nimport { test } from 'node:test';\nimport assert from 'node:assert';\n\ntest('add', () => {\n assert.strictEqual(add(1, 2), 3);\n});\n" } } - ], - "stopReason": "toolUse", - "usage": { "input": 200, "output": 70 } - } - } - ] -} -``` - -- [ ] **Step 4: Re-run fixture tests to verify all 4 fixtures pass** - -Run: `npm run test:fixtures` -Expected: 4 passed, 0 failed - -- [ ] **Step 5: Commit** - -```bash -git add tests/fixtures/recordings/ -git commit -m "feat(ci): add additional fixture recordings for multi-turn and error scenarios" -``` - ---- - -## Chunk 4: Live Tests (Stub) + npm Scripts - -### Task 6: Live Test Stubs - -**Files:** -- Create: `tests/live/run.ts` -- Create: `tests/live/test-anthropic-roundtrip.ts` -- Modify: `package.json` (add remaining scripts) - -- [ ] **Step 1: Create live test runner** - -```typescript -// tests/live/run.ts -// Runs real LLM integration tests. Only executes when SF_LIVE_TESTS=1. -// These tests cost real money — used in the Prod gate only. -// -// Usage: SF_LIVE_TESTS=1 node --experimental-strip-types tests/live/run.ts - -if (process.env.SF_LIVE_TESTS !== "1") { - console.log("Skipping live tests (set SF_LIVE_TESTS=1 to enable)"); - process.exit(0); -} - -import { readdirSync } from "fs"; -import { execFileSync } from "child_process"; -import { fileURLToPath } from "url"; -import { dirname, join } from "path"; - -const dir = dirname(fileURLToPath(import.meta.url)); -const tests = readdirSync(dir).filter((f) => f.startsWith("test-") && f.endsWith(".ts")); - -let passed = 0; -let failed = 0; - -for (const test of tests) { - const path = join(dir, test); - try { - execFileSync("node", ["--experimental-strip-types", path], { - encoding: "utf8", - stdio: "pipe", - timeout: 120_000, - env: { ...process.env }, - }); - console.log(`✓ ${test}`); - passed++; - } catch (err: any) { - console.error(`✗ ${test}`); - console.error(err.stdout || ""); - console.error(err.stderr || ""); - failed++; - } -} - -console.log(`\n${passed} passed, ${failed} failed`); -if (failed > 0) process.exit(1); -``` - -- [ ] **Step 2: Create Anthropic roundtrip test** - -```typescript -// tests/live/test-anthropic-roundtrip.ts -// Sends a minimal request to the Anthropic API and verifies a response. -// Requires ANTHROPIC_API_KEY in environment. - -const apiKey = process.env.ANTHROPIC_API_KEY; -if (!apiKey) { - console.error("ANTHROPIC_API_KEY not set"); - process.exit(1); -} - -const response = await fetch("https://api.anthropic.com/v1/messages", { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-api-key": apiKey, - "anthropic-version": "2023-06-01", - }, - body: JSON.stringify({ - model: "claude-haiku-4-5", - max_tokens: 32, - messages: [{ role: "user", content: "Reply with exactly: OK" }], - }), -}); - -if (!response.ok) { - const body = await response.text(); - console.error(`API error ${response.status}: ${body}`); - process.exit(1); -} - -const data = (await response.json()) as { content: Array<{ text: string }> }; -const text = data.content[0]?.text; - -if (!text || text.length === 0) { - console.error("Empty response from API"); - process.exit(1); -} - -console.log(`Anthropic roundtrip OK: "${text.substring(0, 50)}"`); -``` - -- [ ] **Step 3: Create OpenAI roundtrip test** - -```typescript -// tests/live/test-openai-roundtrip.ts -// Sends a minimal request to the OpenAI API and verifies a response. -// Requires OPENAI_API_KEY in environment. - -const apiKey = process.env.OPENAI_API_KEY; -if (!apiKey) { - console.error("OPENAI_API_KEY not set"); - process.exit(1); -} - -const response = await fetch("https://api.openai.com/v1/chat/completions", { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${apiKey}`, - }, - body: JSON.stringify({ - model: "gpt-4o-mini", - max_tokens: 32, - messages: [{ role: "user", content: "Reply with exactly: OK" }], - }), -}); - -if (!response.ok) { - const body = await response.text(); - console.error(`API error ${response.status}: ${body}`); - process.exit(1); -} - -const data = (await response.json()) as { choices: Array<{ message: { content: string } }> }; -const text = data.choices[0]?.message?.content; - -if (!text || text.length === 0) { - console.error("Empty response from API"); - process.exit(1); -} - -console.log(`OpenAI roundtrip OK: "${text.substring(0, 50)}"`); -``` - -- [ ] **Step 4: Add remaining scripts to package.json** - -Add to `package.json` `scripts`: -```json -"test:fixtures:record": "SF_FIXTURE_MODE=record node --experimental-strip-types tests/fixtures/record.ts", -"test:live": "SF_LIVE_TESTS=1 node --experimental-strip-types tests/live/run.ts", -"pipeline:version-stamp": "node scripts/version-stamp.mjs", -"docker:build-runtime": "docker build --target runtime -t ghcr.io/singularity-forge/sf-run .", -"docker:build-builder": "docker build --target builder -t ghcr.io/singularity-forge/sf-ci-builder ." -``` - -- [ ] **Step 5: Verify live tests skip without env var** - -Run: `npm run test:live` -Expected: `Skipping live tests (set SF_LIVE_TESTS=1 to enable)` and exit 0 - -- [ ] **Step 6: Commit** - -```bash -git add tests/live/ package.json -git commit -m "feat(ci): add live LLM test stubs and remaining npm scripts" -``` - ---- - -## Chunk 5: GitHub Actions Workflows - -### Task 7: Pipeline Workflow - -**Files:** -- Create: `.github/workflows/pipeline.yml` - -- [ ] **Step 1: Write the pipeline workflow** - -```yaml -# .github/workflows/pipeline.yml -# Three-stage promotion pipeline: Dev → Test → Prod -# Triggers after ci.yml succeeds on main branch. - -name: Release Pipeline - -on: - workflow_run: - workflows: ["CI"] - types: [completed] - branches: [main] - -concurrency: - group: pipeline-${{ github.sha }} - cancel-in-progress: false - -jobs: - # ─── DEV STAGE ───────────────────────────────────────────── - dev-publish: - if: ${{ github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-latest - container: - image: ghcr.io/singularity-forge/sf-ci-builder:latest # Pin to date tag after first build - environment: dev - outputs: - dev-version: ${{ steps.stamp.outputs.version }} - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - ref: ${{ github.event.workflow_run.head_sha }} - fetch-depth: 0 - - - name: Setup npm registry - run: echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Install dependencies - run: npm ci - - - name: Build - run: npm run build - - - name: Stamp dev version - id: stamp - run: | - node scripts/version-stamp.mjs - VERSION=$(node -p "require('./package.json').version") - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "Dev version: $VERSION" - - - name: Publish to npm with @dev tag - run: npm publish --tag dev - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Smoke test published package - run: | - mkdir /tmp/smoke-test && cd /tmp/smoke-test - npm init -y - npm install sf-run@dev - npx sf --version - - # ─── TEST STAGE ──────────────────────────────────────────── - test-verify: - needs: dev-publish - runs-on: ubuntu-latest - environment: test - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - ref: ${{ github.event.workflow_run.head_sha }} - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: "22" - registry-url: "https://registry.npmjs.org" - - - name: Install published dev package globally - run: npm install -g sf-run@dev - - - name: Install dev dependencies for test runners - run: npm ci - - - name: Run CLI smoke tests - run: npm run test:smoke - env: - SF_SMOKE_BINARY: sf # Use globally installed binary, not npx - - - name: Run fixture replay tests - run: npm run test:fixtures - - - name: Promote to @next - run: npm dist-tag add sf-run@${{ needs.dev-publish.outputs.dev-version }} next - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Build and push runtime Docker image - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - docker build --target runtime \ - --build-arg SF_VERSION=${{ needs.dev-publish.outputs.dev-version }} \ - -t ghcr.io/singularity-forge/sf-run:next \ - -t ghcr.io/singularity-forge/sf-run:${{ needs.dev-publish.outputs.dev-version }} \ - . - docker push ghcr.io/singularity-forge/sf-run:next - docker push ghcr.io/singularity-forge/sf-run:${{ needs.dev-publish.outputs.dev-version }} - - # ─── PROD STAGE ──────────────────────────────────────────── - prod-release: - needs: [dev-publish, test-verify] - runs-on: ubuntu-latest - environment: prod # Requires manual approval - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - ref: ${{ github.event.workflow_run.head_sha }} - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: "22" - registry-url: "https://registry.npmjs.org" - - - name: Run live LLM tests (optional) - if: ${{ vars.RUN_LIVE_TESTS == 'true' }} - run: | - npm ci - npm run build - SF_LIVE_TESTS=1 npm run test:live - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - - - name: Promote to @latest - run: npm dist-tag add sf-run@${{ needs.dev-publish.outputs.dev-version }} latest - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Tag and push Docker images - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - docker pull ghcr.io/singularity-forge/sf-run:${{ needs.dev-publish.outputs.dev-version }} - docker tag ghcr.io/singularity-forge/sf-run:${{ needs.dev-publish.outputs.dev-version }} ghcr.io/singularity-forge/sf-run:latest - docker push ghcr.io/singularity-forge/sf-run:latest - - - name: Create GitHub Release - run: | - gh release create v${{ needs.dev-publish.outputs.dev-version }} \ - --generate-notes \ - --title "v${{ needs.dev-publish.outputs.dev-version }}" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Post-publish smoke test - run: | - mkdir /tmp/prod-smoke && cd /tmp/prod-smoke - npm init -y - npm install sf-run@latest - npx sf --version - - # ─── CI BUILDER IMAGE (conditional) ──────────────────────── - update-builder: - if: | - github.event.workflow_run.conclusion == 'success' && - contains(toJSON(github.event.workflow_run.head_commit.modified), 'Dockerfile') - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - ref: ${{ github.event.workflow_run.head_sha }} - - - name: Generate date tag - id: tag - run: echo "date=$(date +%Y-%m-%d)" >> "$GITHUB_OUTPUT" - - - name: Build and push CI builder image - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - docker build --target builder \ - -t ghcr.io/singularity-forge/sf-ci-builder:latest \ - -t ghcr.io/singularity-forge/sf-ci-builder:${{ steps.tag.outputs.date }} \ - . - docker push ghcr.io/singularity-forge/sf-ci-builder:latest - docker push ghcr.io/singularity-forge/sf-ci-builder:${{ steps.tag.outputs.date }} - - - name: Verify builder image - run: | - docker run --rm ghcr.io/singularity-forge/sf-ci-builder:latest node --version - docker run --rm ghcr.io/singularity-forge/sf-ci-builder:latest rustc --version -``` - -- [ ] **Step 2: Validate YAML syntax** - -Run: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/pipeline.yml'))"` -Expected: No errors - -- [ ] **Step 3: Commit** - -```bash -git add .github/workflows/pipeline.yml -git commit -m "feat(ci): add three-stage promotion pipeline workflow" -``` - ---- - -### Task 8: Dev Version Cleanup Workflow - -**Files:** -- Create: `.github/workflows/cleanup-dev-versions.yml` - -- [ ] **Step 1: Write the cleanup workflow** - -```yaml -# .github/workflows/cleanup-dev-versions.yml -# Weekly cleanup of old -dev. npm versions to prevent registry bloat. -# Unpublishes dev versions older than 30 days. - -name: Cleanup Dev Versions - -on: - schedule: - - cron: "0 6 * * 1" # Every Monday at 06:00 UTC - workflow_dispatch: {} # Allow manual trigger - -jobs: - cleanup: - runs-on: ubuntu-latest - - steps: - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: "22" - registry-url: "https://registry.npmjs.org" - - - name: Remove old dev versions - run: | - VERSIONS=$(npm view sf-run versions --json 2>/dev/null || echo "[]") - - DEV_VERSIONS=$(echo "$VERSIONS" | node -e " - const stdin = require('fs').readFileSync('/dev/stdin', 'utf8'); - const versions = JSON.parse(stdin); - for (const v of versions) { - if (v.includes('-dev.')) { - console.log(v); - } - } - ") - - if [ -z "$DEV_VERSIONS" ]; then - echo "No dev versions to clean up" - exit 0 - fi - - THIRTY_DAYS_MS=2592000000 - - for VERSION in $DEV_VERSIONS; do - PUBLISH_TIME=$(npm view "sf-run@$VERSION" time --json 2>/dev/null || echo "") - - if [ -n "$PUBLISH_TIME" ]; then - AGE_MS=$(node -e " - const t = JSON.parse('$PUBLISH_TIME'); - console.log(Date.now() - new Date(t).getTime()); - " 2>/dev/null || echo "0") - - if [ "$AGE_MS" -gt "$THIRTY_DAYS_MS" ]; then - echo "Unpublishing sf-run@$VERSION" - npm unpublish "sf-run@$VERSION" || echo "Failed to unpublish $VERSION" - else - echo "Keeping sf-run@$VERSION (within 30 days)" - fi - fi - done - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} -``` - -- [ ] **Step 2: Validate YAML syntax** - -Run: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/cleanup-dev-versions.yml'))"` -Expected: No errors - -- [ ] **Step 3: Commit** - -```bash -git add .github/workflows/cleanup-dev-versions.yml -git commit -m "feat(ci): add weekly dev version cleanup workflow" -``` - ---- - -## Chunk 6: Recording Helper + Final Integration - -### Task 9: Fixture Recording Helper - -**Files:** -- Create: `tests/fixtures/record.ts` - -- [ ] **Step 1: Create the recording helper** - -```typescript -// tests/fixtures/record.ts -// Helper for recording new LLM fixtures. -// -// Usage: -// SF_FIXTURE_MODE=record \ -// SF_FIXTURE_DIR=./tests/fixtures/recordings \ -// node --experimental-strip-types tests/fixtures/record.ts -// -// This is a developer tool, not used in CI. -// After recording, review and commit the generated fixture JSON. - -import { getFixtureMode, getFixtureDir } from "./provider.ts"; - -const mode = getFixtureMode(); -const dir = getFixtureDir(); - -if (mode !== "record") { - console.error("Recording requires SF_FIXTURE_MODE=record"); - console.error(""); - console.error("Usage:"); - console.error(" SF_FIXTURE_MODE=record SF_FIXTURE_DIR=./tests/fixtures/recordings \\"); - console.error(" node --experimental-strip-types tests/fixtures/record.ts"); - process.exit(1); -} - -console.log("Fixture recording mode enabled"); -console.log(`Recordings will be saved to: ${dir}`); -console.log(""); -console.log("To record a fixture:"); -console.log("1. Set SF_FIXTURE_MODE=record in your environment"); -console.log("2. Run your SF session normally"); -console.log("3. The FixtureProvider will intercept and save all LLM calls"); -console.log("4. Review the generated JSON in the recordings directory"); -console.log("5. Commit the fixture to version control"); -console.log(""); -console.log("Note: The FixtureProvider must be integrated into the"); -console.log("agent session startup to intercept real API calls."); -console.log("See tests/fixtures/provider.ts for the integration API."); -``` - -- [ ] **Step 2: Commit** - -```bash -git add tests/fixtures/record.ts -git commit -m "feat(ci): add fixture recording helper with usage instructions" -``` - ---- - -### Task 10: Final Integration Verification - -**Prerequisite:** All work should be on the `ci-cd` branch (created from `main` before starting Task 1). - -- [ ] **Step 1: Run the full test suite** - -```bash -npm run test:smoke -npm run test:fixtures -npm run test:live -``` - -Expected: -- Smoke tests: 3 passed -- Fixture tests: 1 passed -- Live tests: Skipped (no `SF_LIVE_TESTS=1`) - -- [ ] **Step 2: Validate all workflow YAML files** - -```bash -python3 -c " -import yaml, glob -for f in glob.glob('.github/workflows/*.yml'): - yaml.safe_load(open(f)) - print(f'OK: {f}') -" -``` - -Expected: All `.yml` files parse without errors - -- [ ] **Step 3: Verify git status is clean** - -Run: `git status` -Expected: Nothing to commit, working tree clean - -- [ ] **Step 4: Review commit history** - -Run: `git log --oneline ci-cd ^main` -Expected: ~10 commits, each self-contained and descriptive - ---- - -## Post-Implementation: GitHub Configuration (Manual) - -These steps require repo admin access and cannot be automated: - -1. **Create GitHub Environments:** - - `dev` — no protection rules - - `test` — no protection rules - - `prod` — add required reviewers (maintainer list) - -2. **Add secrets:** - - `NPM_TOKEN` → all environments - - `ANTHROPIC_API_KEY` → prod only - - `OPENAI_API_KEY` → prod only - -3. **Add environment variable:** - - `RUN_LIVE_TESTS` → `false` by default on `prod` (set to `true` to enable) - -4. **Enable GHCR:** - - Ensure GitHub Container Registry is enabled for the `sf-build` org - -5. **Test the pipeline end-to-end:** - - Merge a test PR to `main` - - Watch Dev stage publish to `@dev` - - Watch Test stage auto-promote to `@next` - - Manually approve Prod to promote to `@latest` diff --git a/docs/dev/superpowers/specs/2026-03-17-cicd-pipeline-design.md b/docs/dev/superpowers/specs/2026-03-17-cicd-pipeline-design.md deleted file mode 100644 index 5ae552982..000000000 --- a/docs/dev/superpowers/specs/2026-03-17-cicd-pipeline-design.md +++ /dev/null @@ -1,357 +0,0 @@ -# CI/CD Pipeline Design — SF - -## Overview - -A three-stage promotion pipeline for SF that moves merged PRs through Dev → Test → Prod using npm dist-tags as environment markers, GitHub Environments for approval gates, and Docker images for both CI acceleration and end-user distribution. - -## Goals - -1. Every merged PR is immediately installable via `npx sf-run@dev` -2. Verified builds auto-promote to `@next` for early adopters -3. Production releases require manual approval and optional live-LLM validation -4. CI builds are fast and reproducible via pre-built Docker builder image -5. End users can run SF via Docker as an alternative to npm -6. LLM-dependent behavior is testable without API calls via recorded fixtures - -## Non-Goals - -- Replacing the existing PR gate workflow (`ci.yml`) -- Replacing the native binary cross-compilation workflow (`build-native.yml`) -- Cross-platform native binary builds (macOS/Windows remain on `build-native.yml`) -- Hosting SF as a web service -- Automated prompt regression testing (future work) - -## Pipeline Architecture - -``` -┌─────────────────────────────────────────────────────────────┐ -│ PR Merged to main │ -│ ci.yml runs (build, test, typecheck) │ -└──────────────────────────┬──────────────────────────────────┘ - ▼ (workflow_run: ci.yml success) -┌──────────────────────────────────────────────────────────────┐ -│ STAGE: DEV Environment: dev │ -│ │ -│ 1. Version stamp: -dev. │ -│ 2. npm publish sf-run@-dev. --tag dev │ -│ 3. Smoke test: npx sf-run@dev --version │ -│ │ -│ Note: Build/test/typecheck already ran in ci.yml │ -│ Docker: Build CI builder image (only if Dockerfile changed) │ -└──────────────────────────┬──────────────────────────────────┘ - ▼ (auto-promote if all green) -┌──────────────────────────────────────────────────────────────┐ -│ STAGE: TEST Environment: test │ -│ │ -│ 1. Install sf-run@dev from registry │ -│ 2. CLI smoke tests (--version, init, help, config) │ -│ 3. Dry-run fixture suite (recorded LLM conversations) │ -│ - Agent session replay with fixture provider │ -│ - Tool use round-trips verified │ -│ - Extension loading validated │ -│ 4. npm dist-tag add sf-run@ next │ -│ │ -│ Docker: Build + push runtime image to GHCR as :next │ -└──────────────────────────┬──────────────────────────────────┘ - ▼ (manual approval required) -┌──────────────────────────────────────────────────────────────┐ -│ STAGE: PROD Environment: prod │ -│ │ -│ 1. (Optional) Real LLM integration tests │ -│ - Gated behind workflow input flag │ -│ - Uses ANTHROPIC_API_KEY / OPENAI_API_KEY secrets │ -│ - Budget-capped: small models, short conversations │ -│ 2. npm dist-tag add sf-run@ latest │ -│ 3. GitHub Release created with changelog │ -│ 4. Docker: tag runtime image as :latest + :v │ -│ 5. Post-publish smoke test against @latest │ -└──────────────────────────────────────────────────────────────┘ -``` - -### Version Strategy - -| Dist-tag | When published | Version format | Risk level | -|----------|---------------|----------------|------------| -| `@dev` | Every merged PR | `2.27.0-dev.a3f2c1b` | Bleeding edge | -| `@next` | Auto-promoted from Dev | Same version, new tag | Candidate | -| `@latest` | Manually approved from Test | Same version, new tag | Production | - -The `-dev.` prerelease identifier is distinct from the existing `-next.` convention used in `build-native.yml`. The two pipelines do not overlap — `build-native.yml` only triggers on `v*` tags and checks for `-next.` to determine npm dist-tag. The `-dev.` versions are published exclusively by `pipeline.yml`. - -### Native Binary Strategy for Dev Publishes - -Dev versions (`@dev` tag) use the native binaries from the most recent stable `build-native.yml` release. The `optionalDependencies` in `package.json` use `>=` ranges, so a `-dev.` version of `sf-run` resolves the latest stable `@sf-build/engine-*` packages from the registry. - -If a PR modifies Rust native crate code (`rust-engine/` directory), the dev publish will bundle stale native binaries. This is acceptable because: -- Native crate changes are infrequent and always accompanied by a `v*` tag release -- The Test stage validates the installed package works end-to-end -- Full native binary validation happens via `build-native.yml` on the version tag - -### Concurrency Control - -```yaml -concurrency: - group: pipeline-${{ github.sha }} - cancel-in-progress: false -``` - -Policy: -- Each pipeline run is keyed to its commit SHA — no two runs for the same commit race -- Newer merges do NOT cancel in-progress promotions — a version already in the Test stage completes its promotion -- If Run A is promoting version X to `@next` while Run B publishes version Y to `@dev`, they operate independently — `@next` and `@dev` point to different versions, which is correct -- The Prod stage always promotes whatever version is currently at `@next`, so approving promotion after a newer version has already moved to `@next` promotes the newer one (last-writer-wins, which is the desired behavior) - -### Failure Modes & Recovery - -| Failure | Impact | Recovery | -|---------|--------|----------| -| Dev publish succeeds, smoke test fails | Broken version on `@dev` tag | Next successful merge overwrites `@dev`. Manual fix: `npm dist-tag add sf-run@ dev` | -| Test stage fails after promoting to `@next` | Broken version on `@next` tag | Manual: `npm dist-tag add sf-run@ next`. `@latest` is never affected. | -| Prod promotion publishes `@latest` then found broken | Broken production release | Manual: `npm dist-tag add sf-run@ latest` and `docker tag ghcr.io/singularity-forge/sf-run: latest && docker push`. Post-mortem required. | -| Docker push succeeds, npm dist-tag fails | Images and npm out of sync | Re-run the failed job (GitHub Actions retry). Images are tagged by version so stale tags are harmless. | -| GHCR push fails | No Docker image for this version | Non-blocking — npm publish is the primary distribution. Docker image can be rebuilt manually. | - -Rollback responsibility: any maintainer with npm publish rights and GHCR push access. The Prod environment's required-reviewers list doubles as the rollback-authorized list. - -### Relationship to Existing Workflows - -| File | Trigger | Purpose | Status | -|------|---------|---------|--------| -| `ci.yml` | PR opened/updated, push to main | Pre-merge gate: build, test, typecheck | **Unchanged** | -| `build-native.yml` | `v*` tag or manual dispatch | Cross-compile native binaries for 5 platforms | **Unchanged** | -| `pipeline.yml` | `workflow_run` (after ci.yml succeeds on main) | Post-merge promotion: Dev → Test → Prod | **New** | - -The pipeline triggers via `workflow_run` after `ci.yml` completes successfully on `main`, avoiding duplicate build/test work. The Dev stage only performs version stamping, publishing, and smoke testing. - -## Docker Images - -### Multi-Stage Dockerfile - -Two images from a single `Dockerfile` at the repo root. - -#### CI Builder Image - -- **Name:** `ghcr.io/singularity-forge/sf-ci-builder` -- **Base:** `node:22-bookworm` -- **Contains:** Node 22, Rust stable toolchain, `aarch64-linux-gnu` cross-compiler -- **Size:** ~2 GB -- **Tags:** `:latest`, `:` (date-stamped for rollback) -- **Rebuilt:** Only when `Dockerfile` changes -- **Used by:** `pipeline.yml` Dev stage, optionally `ci.yml` -- **Purpose:** Eliminates 3-5 min toolchain install on every CI run - -The builder image does NOT include Playwright system deps (not needed for current CI jobs). If browser-based E2E tests are added later, Playwright deps can be added at that point. - -#### Builder Image Versioning - -Builder images are tagged with both `:latest` and a date stamp (e.g., `:2026-03-17`). The `pipeline.yml` workflow pins to a specific date-stamped tag. When the Dockerfile is updated, the PR that changes it also updates the tag reference in `pipeline.yml`. This prevents a broken Dockerfile change from silently breaking all subsequent runs. - -#### Runtime Image - -- **Name:** `ghcr.io/singularity-forge/sf-run` -- **Base:** `node:22-slim` -- **Contains:** Node 22, git, `sf-run` installed globally -- **Size:** ~250 MB -- **Tags:** `:latest`, `:next`, `:v2.27.0` -- **Published:** On every Prod promotion -- **Purpose:** `docker run ghcr.io/singularity-forge/sf-run` as alternative to `npx` - -### Why These Base Images - -- **Bookworm for CI:** The Rust native crates depend on vendored `libgit2`, image processing, and cross-compilation to ARM64. Debian Bookworm provides the full toolchain via apt. Alpine breaks due to musl vs glibc incompatibilities with N-API bindings. -- **Slim for runtime:** Only needs Node + git. Native `.node` binaries are prebuilt and bundled in the npm package — no Rust toolchain needed at runtime. - -## LLM Fixture Recording & Replay System - -### Architecture - -The fixture system hooks into the `pi-ai` provider abstraction layer to capture and replay LLM conversations without hitting real APIs. - -``` -Agent Session - │ - ▼ -pi-ai provider abstraction - │ - ▼ -FixtureProvider (intercept layer) - │ - ├── record mode → Real API + save to fixture JSON - │ - └── replay mode → Load fixture JSON (no API call) -``` - -### Integration Design - -The `FixtureProvider` implements the `Provider` interface from `@sf/pi-ai` (the same interface all 20+ built-in providers implement). It registers itself via environment variable detection at provider initialization: - -```typescript -// Pseudocode — actual implementation will follow pi-ai patterns -import type { Provider, StreamingResponse } from "@sf/pi-ai"; - -class FixtureProvider implements Provider { - // In record mode: wraps the real provider, saves responses - // In replay mode: returns saved responses directly - - async *stream(request: ProviderRequest): AsyncGenerator { - if (this.mode === "replay") { - // Yield fixture response chunks (simulated streaming) - yield* this.replayTurn(this.turnIndex++); - } else { - // Proxy to real provider, capture response - const chunks = []; - for await (const chunk of this.realProvider.stream(request)) { - chunks.push(chunk); - yield chunk; - } - this.saveTurn(request, chunks); - } - } -} -``` - -Key integration details: -- **Streaming:** Fixture replay simulates streaming by yielding saved response chunks with minimal delay. This exercises the same consumer code paths as real streaming. -- **Registration:** When `SF_FIXTURE_MODE` is set, the fixture provider wraps the configured real provider. No changes to provider selection logic needed. -- **Provider-agnostic:** Fixtures are captured at the `Provider` interface level (above HTTP transport), so they work regardless of which underlying provider was used during recording. - -### Modes - -| Mode | Trigger | Behavior | -|------|---------|----------| -| **Record** | `SF_FIXTURE_MODE=record SF_FIXTURE_DIR=./fixtures` | Wraps real provider, saves request/response pairs | -| **Replay** | `SF_FIXTURE_MODE=replay SF_FIXTURE_DIR=./fixtures` | Returns saved responses, zero API calls | -| **Off** | Default (no env vars) | Normal operation, no interception | - -### Fixture Format - -One JSON file per recorded session: - -```json -{ - "name": "agent-creates-file", - "recorded": "2026-03-17T00:00:00Z", - "provider": "anthropic", - "model": "claude-sonnet-4-6", - "turns": [ - { - "request": { - "messages": [{ "role": "user", "content": "Create hello.ts" }], - "tools": ["Write", "Read"], - "model": "claude-sonnet-4-6" - }, - "response": { - "content": [ - { "type": "text", "text": "I'll create hello.ts for you." }, - { "type": "tool_use", "name": "Write", "input": { "file_path": "hello.ts", "content": "console.log('hello')" } } - ], - "stopReason": "toolUse", - "usage": { "input": 150, "output": 45 } - } - } - ] -} -``` - -### Matching Strategy - -Turn-index based. Response N is served for request N in sequence. If the conversation diverges from the fixture (e.g., unexpected turn count), the test fails explicitly with a descriptive error rather than silently producing wrong results. - -Why not request-body hashing: request bodies contain timestamps, random IDs, and system prompt variations that cause brittle mismatches. - -Why not a generic HTTP VCR: The `pi-ai` layer abstracts 20+ providers with different wire formats. Intercepting above the transport means fixtures are provider-agnostic. - -### What Gets Tested via Fixtures - -- Agent session lifecycle (start → tool calls → completion) -- Tool dispatch and response handling -- Multi-turn conversation flow -- Extension loading and routing -- Error handling paths (fixtures can include error responses) - -### What Does NOT Get Tested (Deferred to Live Gate) - -- Model output quality -- Prompt regression -- New tool compatibility with live APIs - -### Fixture Storage - -Committed to repo under `tests/fixtures/recordings/`. Each fixture is 5-50KB of JSON. Recording is a manual developer action, not automated in CI. - -### Dev Version Cleanup - -Old `-dev.` versions accumulate on npm with every merged PR. A scheduled workflow (`cleanup-dev-versions.yml`) runs weekly and unpublishes dev versions older than 30 days via `npm unpublish sf-run@`. This prevents registry bloat while keeping recent dev versions available. - -## New Files & Scripts - -### Directory Structure - -``` -tests/ -├── smoke/ # CLI smoke tests (Stage: Test) -│ ├── run.ts -│ ├── test-version.ts -│ ├── test-help.ts -│ └── test-init.ts -│ -├── fixtures/ # Recorded LLM replay tests (Stage: Test) -│ ├── run.ts # Test runner -│ ├── record.ts # Recording helper -│ ├── provider.ts # FixtureProvider intercept layer -│ └── recordings/ -│ ├── agent-creates-file.json -│ ├── agent-reads-and-edits.json -│ ├── agent-handles-error.json -│ └── agent-multi-turn-tools.json -│ -├── live/ # Real LLM tests (Stage: Prod, optional) -│ ├── run.ts -│ ├── test-anthropic-roundtrip.ts -│ └── test-openai-roundtrip.ts -│ -scripts/ -├── version-stamp.mjs # Stamps -dev. - -Dockerfile # Multi-stage: builder + runtime -.github/workflows/pipeline.yml # Promotion pipeline -.github/workflows/cleanup-dev-versions.yml # Weekly dev version pruning -``` - -All test files use `.ts` with `--experimental-strip-types` for consistency with the existing test convention in the project. - -### New npm Scripts - -```json -{ - "test:smoke": "node --experimental-strip-types tests/smoke/run.ts", - "test:fixtures": "node --experimental-strip-types tests/fixtures/run.ts", - "test:fixtures:record": "SF_FIXTURE_MODE=record node --experimental-strip-types tests/fixtures/record.ts", - "test:live": "SF_LIVE_TESTS=1 node --experimental-strip-types tests/live/run.ts", - "pipeline:version-stamp": "node scripts/version-stamp.mjs", - "docker:build-runtime": "docker build --target runtime -t ghcr.io/singularity-forge/sf-run .", - "docker:build-builder": "docker build --target builder -t ghcr.io/singularity-forge/sf-ci-builder ." -} -``` - -## GitHub Configuration - -| Setting | Value | -|---------|-------| -| Environment: `dev` | No protection rules | -| Environment: `test` | No protection rules (auto-promote) | -| Environment: `prod` | Required reviewers: maintainers | -| Secret: `NPM_TOKEN` | All environments | -| Secret: `ANTHROPIC_API_KEY` | Prod only | -| Secret: `OPENAI_API_KEY` | Prod only | -| GHCR | Enabled for org | - -## Success Criteria - -1. A merged PR is installable via `npx sf-run@dev` within 15 minutes (assumes warm CI builder image cache) -2. Fixture replay tests complete in under 60 seconds with zero API calls -3. The full Dev → Test promotion completes without human intervention -4. Prod promotion is blocked until a maintainer explicitly approves -5. `docker run ghcr.io/singularity-forge/sf-run --version` returns the correct version -6. Existing `ci.yml` and `build-native.yml` workflows continue to work unchanged -7. CI builder image reduces toolchain setup from ~3-5 min to ~30s pull diff --git a/docs/user-docs/getting-started.md b/docs/user-docs/getting-started.md index 893bbfe86..0e1bbb9e1 100644 --- a/docs/user-docs/getting-started.md +++ b/docs/user-docs/getting-started.md @@ -8,7 +8,7 @@ SF is an AI coding agent that handles planning, execution, verification, and shi | Requirement | Minimum | Recommended | |-------------|---------|-------------| -| **[Node.js](https://nodejs.org/)** | 22.0.0 | 24 LTS | +| **[Node.js](https://nodejs.org/)** | 24.0.0 | 24 LTS | | **[Git](https://git-scm.com/)** | 2.20+ | Latest | | **LLM API key** | Any supported provider | Anthropic (Claude) | diff --git a/docs/user-docs/node-lts-macos.md b/docs/user-docs/node-lts-macos.md index 04f285863..ffaf5a642 100644 --- a/docs/user-docs/node-lts-macos.md +++ b/docs/user-docs/node-lts-macos.md @@ -2,7 +2,7 @@ If you installed Node.js via Homebrew (`brew install node`), you're tracking the **latest current release** — which can include odd-numbered development versions (e.g. 23.x, 25.x). These aren't LTS and may have breaking changes or instability. -SF requires Node.js **v22 or later** and works best on an **LTS (even-numbered) release**. This guide shows how to pin Node 24 LTS using Homebrew. +SF requires Node.js **v24 or later** and works best on an **LTS (even-numbered) release**. This guide shows how to pin Node 24 LTS using Homebrew. ## Check your current version diff --git a/docs/user-docs/troubleshooting.md b/docs/user-docs/troubleshooting.md index 3489bc54d..6c20ab2f6 100644 --- a/docs/user-docs/troubleshooting.md +++ b/docs/user-docs/troubleshooting.md @@ -71,7 +71,7 @@ source ~/.zshrc **Common causes:** - Missing workspace packages — fixed in v2.10.4+ - `postinstall` hangs on Linux (Playwright `--with-deps` triggering sudo) — fixed in v2.3.6+ -- Node.js version too old — requires ≥ 22.0.0 +- Node.js version too old — requires ≥ 24.0.0 ### Provider errors during auto mode diff --git a/docs/zh-CN/user-docs/getting-started.md b/docs/zh-CN/user-docs/getting-started.md index d74ab7300..0db0cc0d0 100644 --- a/docs/zh-CN/user-docs/getting-started.md +++ b/docs/zh-CN/user-docs/getting-started.md @@ -8,7 +8,7 @@ SF 是一个 AI 编程代理,负责规划、执行、验证和交付,让你 | 要求 | 最低版本 | 推荐版本 | |------|----------|----------| -| **[Node.js](https://nodejs.org/)** | 22.0.0 | 24 LTS | +| **[Node.js](https://nodejs.org/)** | 24.0.0 | 24 LTS | | **[Git](https://git-scm.com/)** | 2.20+ | 最新版 | | **LLM API key** | 任意受支持提供商 | Anthropic(Claude) | diff --git a/docs/zh-CN/user-docs/node-lts-macos.md b/docs/zh-CN/user-docs/node-lts-macos.md index efd12ca63..8655a8426 100644 --- a/docs/zh-CN/user-docs/node-lts-macos.md +++ b/docs/zh-CN/user-docs/node-lts-macos.md @@ -2,7 +2,7 @@ 如果你是通过 Homebrew 安装 Node.js(`brew install node`),那你跟踪的是**当前最新正式版本**,其中可能包含奇数版本的开发分支(例如 23.x、25.x)。这些版本并不是 LTS,可能带来破坏性变更或稳定性问题。 -SF 要求 Node.js **v22 或更高版本**,并且在 **LTS(偶数版本)** 上运行效果最好。本指南展示如何用 Homebrew 固定到 Node 24 LTS。 +SF 要求 Node.js **v24 或更高版本**,并且在 **LTS(偶数版本)** 上运行效果最好。本指南展示如何用 Homebrew 固定到 Node 24 LTS。 ## 检查当前版本 diff --git a/docs/zh-CN/user-docs/troubleshooting.md b/docs/zh-CN/user-docs/troubleshooting.md index e73e2ff6e..d70a92a84 100644 --- a/docs/zh-CN/user-docs/troubleshooting.md +++ b/docs/zh-CN/user-docs/troubleshooting.md @@ -75,7 +75,7 @@ source ~/.zshrc - 缺少 workspace packages:已在 v2.10.4+ 修复 - Linux 上 `postinstall` 卡住(Playwright `--with-deps` 触发 sudo):已在 v2.3.6+ 修复 -- Node.js 版本过低:要求 ≥ 22.0.0 +- Node.js 版本过低:要求 ≥ 24.0.0 ### 自动模式中的 provider 错误 diff --git a/gitbook/README.md b/gitbook/README.md index 2582b9ecb..e80306a53 100644 --- a/gitbook/README.md +++ b/gitbook/README.md @@ -60,6 +60,6 @@ The recommended workflow: run auto mode in one terminal, steer from another. See ## Requirements -- **Node.js** 22.0.0 or later (24 LTS recommended) +- **Node.js** 24.0.0 or later (24 LTS recommended) - **Git** installed and configured - An API key for at least one LLM provider (or use browser sign-in for Anthropic/GitHub Copilot) diff --git a/gitbook/getting-started/installation.md b/gitbook/getting-started/installation.md index 5b5e69816..0a717e028 100644 --- a/gitbook/getting-started/installation.md +++ b/gitbook/getting-started/installation.md @@ -6,7 +6,7 @@ npm install -g sf-run ``` -Requires **Node.js 22.0.0 or later** (24 LTS recommended) and **Git**. +Requires **Node.js 24.0.0 or later** (24 LTS recommended) and **Git**. {% hint style="info" %} **`command not found: sf`?** Your shell may not have npm's global bin directory in `$PATH`. Run `npm prefix -g` to find it, then add `$(npm prefix -g)/bin` to your PATH. See [Troubleshooting](../reference/troubleshooting.md) for details. diff --git a/mintlify-docs/guides/troubleshooting.mdx b/mintlify-docs/guides/troubleshooting.mdx index 640ec0815..c38704021 100644 --- a/mintlify-docs/guides/troubleshooting.mdx +++ b/mintlify-docs/guides/troubleshooting.mdx @@ -91,9 +91,9 @@ It checks file structure, referential integrity, completion state consistency, g - **Cause:** SF v2.45+ checks for Node.js >= 22 and git availability at startup. + **Cause:** SF v2.45+ checks for Node.js >= 24 and git availability at startup. - **Fix:** Install Node.js 22+ (24 LTS recommended) and ensure `git` is in your PATH. + **Fix:** Install Node.js 24+ (24 LTS recommended) and ensure `git` is in your PATH. diff --git a/package-lock.json b/package-lock.json index 25d6ef0fb..2a5a7cd21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,7 +77,7 @@ "vitest": "^4.1.5" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.83", @@ -16279,7 +16279,7 @@ "typescript": "^5.4.0" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" } }, "packages/daemon/node_modules/@anthropic-ai/sdk": { @@ -16317,13 +16317,16 @@ "typescript": "^5.4.0" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" } }, "packages/native": { "name": "@singularity-forge/native", "version": "2.75.0", "license": "MIT", + "engines": { + "node": ">=24.0.0" + }, "optionalDependencies": { "@singularity-forge/engine-darwin-arm64": ">=2.75.0", "@singularity-forge/engine-darwin-x64": ">=2.75.0", @@ -16334,7 +16337,10 @@ }, "packages/pi-agent-core": { "name": "@singularity-forge/pi-agent-core", - "version": "2.75.0" + "version": "2.75.0", + "engines": { + "node": ">=24.0.0" + } }, "packages/pi-ai": { "name": "@singularity-forge/pi-ai", @@ -16360,6 +16366,9 @@ }, "devDependencies": { "@smithy/node-http-handler": "^4.5.0" + }, + "engines": { + "node": ">=24.0.0" } }, "packages/pi-ai/node_modules/@smithy/node-http-handler": { @@ -16407,6 +16416,9 @@ "@types/hosted-git-info": "^3.0.5", "@types/proper-lockfile": "^4.1.4", "@types/sql.js": "^1.4.9" + }, + "engines": { + "node": ">=24.0.0" } }, "packages/pi-coding-agent/node_modules/accepts": { @@ -16712,6 +16724,9 @@ "devDependencies": { "@types/mime-types": "^2.1.4" }, + "engines": { + "node": ">=24.0.0" + }, "optionalDependencies": { "koffi": "^2.9.0" } @@ -16721,7 +16736,7 @@ "version": "2.75.0", "license": "MIT", "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" } }, "studio": { @@ -16736,7 +16751,7 @@ }, "devDependencies": { "@tailwindcss/vite": "^4.2.1", - "@types/node": "^22.18.6", + "@types/node": "^24.12.2", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^5.1.0", @@ -16744,24 +16759,20 @@ "electron-vite": "^5.0.0", "tailwindcss": "^4.2.1", "typescript": "^5.9.3" + }, + "engines": { + "node": ">=24.0.0" } }, "studio/node_modules/@types/node": { - "version": "22.19.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", - "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } - }, - "studio/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" } } } diff --git a/package.json b/package.json index 3710b04bf..f71bfde53 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "configDir": ".sf" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" }, "packageManager": "npm@10.9.3", "scripts": { diff --git a/packages/daemon/package.json b/packages/daemon/package.json index a58ca5518..32b135756 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -39,7 +39,7 @@ "typescript": "^5.4.0" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" }, "files": [ "dist", diff --git a/packages/daemon/src/cli.ts b/packages/daemon/src/cli.ts index 32e78abf1..c096fdc58 100644 --- a/packages/daemon/src/cli.ts +++ b/packages/daemon/src/cli.ts @@ -1,7 +1,6 @@ #!/usr/bin/env node import { parseArgs } from 'node:util'; -import { fileURLToPath } from 'node:url'; -import { resolve, dirname } from 'node:path'; +import { resolve } from 'node:path'; import { resolveConfigPath, loadConfig } from './config.js'; import { Logger } from './logger.js'; import { Daemon } from './daemon.js'; @@ -40,8 +39,7 @@ async function main(): Promise { if (values.install) { const configPath = resolveConfigPath(values.config); - const thisFile = fileURLToPath(import.meta.url); - const scriptPath = resolve(dirname(thisFile), 'cli.js'); + const scriptPath = resolve(import.meta.dirname, 'cli.js'); install({ nodePath: process.execPath, diff --git a/packages/daemon/src/launchd.test.ts b/packages/daemon/src/launchd.test.ts index 57b1ace7b..d9c0637ec 100644 --- a/packages/daemon/src/launchd.test.ts +++ b/packages/daemon/src/launchd.test.ts @@ -75,15 +75,15 @@ describe('generatePlist', () => { }); it('uses the absolute node path from opts', () => { - const opts = basePlistOpts({ nodePath: '/home/user/.nvm/versions/node/v22.0.0/bin/node' }); + const opts = basePlistOpts({ nodePath: '/home/user/.nvm/versions/node/v24.0.0/bin/node' }); const xml = generatePlist(opts); - assert.ok(xml.includes('/home/user/.nvm/versions/node/v22.0.0/bin/node')); + assert.ok(xml.includes('/home/user/.nvm/versions/node/v24.0.0/bin/node')); }); it('includes NVM bin directory in PATH', () => { - const opts = basePlistOpts({ nodePath: '/home/user/.nvm/versions/node/v22.0.0/bin/node' }); + const opts = basePlistOpts({ nodePath: '/home/user/.nvm/versions/node/v24.0.0/bin/node' }); const xml = generatePlist(opts); - assert.ok(xml.includes('/home/user/.nvm/versions/node/v22.0.0/bin')); + assert.ok(xml.includes('/home/user/.nvm/versions/node/v24.0.0/bin')); }); it('sets KeepAlive with SuccessfulExit false', () => { diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index dc43c89f3..d804205d2 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -37,7 +37,7 @@ "typescript": "^5.4.0" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" }, "files": [ "dist", diff --git a/packages/native/package.json b/packages/native/package.json index 9018d6646..55192ad75 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -88,6 +88,9 @@ "files": [ "dist" ], + "engines": { + "node": ">=24.0.0" + }, "optionalDependencies": { "@singularity-forge/engine-darwin-arm64": ">=2.75.0", "@singularity-forge/engine-darwin-x64": ">=2.75.0", diff --git a/packages/pi-agent-core/package.json b/packages/pi-agent-core/package.json index 143d5dedf..d5fadba8c 100644 --- a/packages/pi-agent-core/package.json +++ b/packages/pi-agent-core/package.json @@ -14,5 +14,8 @@ "scripts": { "build": "tsc -p tsconfig.json" }, - "dependencies": {} + "dependencies": {}, + "engines": { + "node": ">=24.0.0" + } } diff --git a/packages/pi-ai/package.json b/packages/pi-ai/package.json index 991ec5aad..b15e372a9 100644 --- a/packages/pi-ai/package.json +++ b/packages/pi-ai/package.json @@ -43,5 +43,8 @@ }, "devDependencies": { "@smithy/node-http-handler": "^4.5.0" + }, + "engines": { + "node": ">=24.0.0" } } diff --git a/packages/pi-ai/scripts/generate-models.ts b/packages/pi-ai/scripts/generate-models.ts index 27ba5403f..afb76eb93 100644 --- a/packages/pi-ai/scripts/generate-models.ts +++ b/packages/pi-ai/scripts/generate-models.ts @@ -1,13 +1,10 @@ #!/usr/bin/env tsx import { writeFileSync } from "fs"; -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; +import { join } from "path"; import { Api, KnownProvider, Model } from "../src/types.js"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const packageRoot = join(__dirname, ".."); +const packageRoot = join(import.meta.dirname, ".."); interface ModelsDevModel { id: string; diff --git a/packages/pi-coding-agent/package.json b/packages/pi-coding-agent/package.json index 162c9be9d..8177cec71 100644 --- a/packages/pi-coding-agent/package.json +++ b/packages/pi-coding-agent/package.json @@ -44,5 +44,8 @@ "@types/hosted-git-info": "^3.0.5", "@types/proper-lockfile": "^4.1.4", "@types/express": "^4.17.21" + }, + "engines": { + "node": ">=24.0.0" } } diff --git a/packages/pi-coding-agent/src/config.ts b/packages/pi-coding-agent/src/config.ts index c50960035..05b09dfef 100644 --- a/packages/pi-coding-agent/src/config.ts +++ b/packages/pi-coding-agent/src/config.ts @@ -1,14 +1,12 @@ import { existsSync, readFileSync } from "fs"; import { homedir } from "os"; import { dirname, join, resolve } from "path"; -import { fileURLToPath } from "url"; // ============================================================================= // Package Detection // ============================================================================= -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const __dirname = import.meta.dirname; /** * Detect if we're running as a Bun compiled binary. diff --git a/packages/pi-coding-agent/src/core/extensions/loader.ts b/packages/pi-coding-agent/src/core/extensions/loader.ts index 796c8baeb..4d1714215 100644 --- a/packages/pi-coding-agent/src/core/extensions/loader.ts +++ b/packages/pi-coding-agent/src/core/extensions/loader.ts @@ -293,8 +293,7 @@ let _aliases: Record | null = null; function getAliases(): Record { if (_aliases) return _aliases; - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const packageIndex = path.resolve(__dirname, "../..", "index.js"); + const packageIndex = path.resolve(import.meta.dirname, "../..", "index.js"); const typeboxEntry = require.resolve("@sinclair/typebox"); const typeboxRoot = typeboxEntry.replace(/[\\/]build[\\/]cjs[\\/]index\.js$/, ""); diff --git a/packages/pi-coding-agent/src/core/lifecycle-hooks.ts b/packages/pi-coding-agent/src/core/lifecycle-hooks.ts index fa103ef79..3b7b145fb 100644 --- a/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +++ b/packages/pi-coding-agent/src/core/lifecycle-hooks.ts @@ -87,6 +87,9 @@ export function collectRuntimeDependencies(installedPath: string, entryPaths: st export function verifyRuntimeDependencies(runtimeDeps: string[], source: string, appName: string): void { const missing: string[] = []; for (const dep of runtimeDeps) { + if (dep === "node") { + continue; + } const result = spawnSync(dep, ["--version"], { encoding: "utf-8", timeout: 5000 }); if (result.error || result.status !== 0) { missing.push(dep); diff --git a/packages/pi-coding-agent/src/core/lsp/index.ts b/packages/pi-coding-agent/src/core/lsp/index.ts index 062020906..5e6d12822 100644 --- a/packages/pi-coding-agent/src/core/lsp/index.ts +++ b/packages/pi-coding-agent/src/core/lsp/index.ts @@ -2,7 +2,6 @@ import * as fs from "node:fs"; import * as fsSync from "node:fs"; import * as path from "node:path"; import { spawn } from "node:child_process"; -import { fileURLToPath } from "node:url"; import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "@singularity-forge/pi-agent-core"; import { ensureFileOpen, @@ -69,8 +68,7 @@ export type { LspServerStatus } from "./client.js"; export type { LspToolDetails } from "./types.js"; export { lspSchema } from "./types.js"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const lspDescription = fsSync.readFileSync(path.join(__dirname, "lsp.md"), "utf-8"); +const lspDescription = fsSync.readFileSync(path.join(import.meta.dirname, "lsp.md"), "utf-8"); // ============================================================================= // Warmup API diff --git a/packages/pi-tui/package.json b/packages/pi-tui/package.json index d8d18e957..20c06069a 100644 --- a/packages/pi-tui/package.json +++ b/packages/pi-tui/package.json @@ -26,5 +26,8 @@ }, "optionalDependencies": { "koffi": "^2.9.0" + }, + "engines": { + "node": ">=24.0.0" } } diff --git a/packages/rpc-client/package.json b/packages/rpc-client/package.json index a7ba0eda1..29e8eb910 100644 --- a/packages/rpc-client/package.json +++ b/packages/rpc-client/package.json @@ -29,6 +29,6 @@ "test": "node --test dist/rpc-client.test.js" }, "engines": { - "node": ">=22.0.0" + "node": ">=24.0.0" } } diff --git a/pkg/package.json b/pkg/package.json index b7637fae1..6e33047c6 100644 --- a/pkg/package.json +++ b/pkg/package.json @@ -1,6 +1,9 @@ { "name": "sf", "version": "2.75.0", + "engines": { + "node": ">=24.0.0" + }, "piConfig": { "name": "sf", "configDir": ".sf" diff --git a/rust-engine/README.md b/rust-engine/README.md index 214927e90..e5907d8a4 100644 --- a/rust-engine/README.md +++ b/rust-engine/README.md @@ -18,7 +18,7 @@ Inspired by [Oh My Pi's pi-natives](https://github.com/can1357/oh-my-pi), adapte ## Prerequisites - **Rust** (stable, 1.70+): https://rustup.rs -- **Node.js** (22.0.0+) +- **Node.js** (24.0.0+) ## Build diff --git a/scripts/compile-tests.mjs b/scripts/compile-tests.mjs index 28105a307..fe59b70cc 100644 --- a/scripts/compile-tests.mjs +++ b/scripts/compile-tests.mjs @@ -105,7 +105,7 @@ async function main() { bundle: false, format: 'esm', platform: 'node', - target: 'node22', + target: 'node24', sourcemap: 'inline', packages: 'external', logLevel: 'warning', diff --git a/scripts/dev-cli.js b/scripts/dev-cli.js index ddf1c28d2..86dc1d295 100644 --- a/scripts/dev-cli.js +++ b/scripts/dev-cli.js @@ -1,10 +1,9 @@ #!/usr/bin/env node import { spawn, spawnSync } from "node:child_process"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { resolve } from "node:path"; -const __dirname = dirname(fileURLToPath(import.meta.url)); +const __dirname = import.meta.dirname; const root = resolve(__dirname, ".."); const sourceBinPath = resolve(root, "bin", "sf-from-source"); const ensureResourcesPath = resolve( diff --git a/scripts/dev.js b/scripts/dev.js index 0eea64072..92403054b 100644 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -9,11 +9,10 @@ */ import { spawn } from 'node:child_process' -import { resolve, dirname } from 'node:path' -import { fileURLToPath } from 'node:url' +import { resolve } from 'node:path' import { createRequire } from 'node:module' -const __dirname = dirname(fileURLToPath(import.meta.url)) +const __dirname = import.meta.dirname const root = resolve(__dirname, '..') const require = createRequire(import.meta.url) const tscBin = require.resolve('typescript/bin/tsc') diff --git a/scripts/install-pi-global.js b/scripts/install-pi-global.js index 9aa8670a6..d25ab1faf 100644 --- a/scripts/install-pi-global.js +++ b/scripts/install-pi-global.js @@ -1,10 +1,9 @@ #!/usr/bin/env node import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import os from 'node:os' -import { dirname, join, resolve } from 'node:path' -import { fileURLToPath } from 'node:url' +import { join, resolve } from 'node:path' -const __dirname = dirname(fileURLToPath(import.meta.url)) +const __dirname = import.meta.dirname const resourcesDir = resolve(__dirname, '..', 'src', 'resources') const piRoot = join(os.homedir(), '.pi') const piAgentDir = join(piRoot, 'agent') diff --git a/scripts/postinstall.js b/scripts/postinstall.js index 021ff244b..9fcc2e4da 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -4,13 +4,12 @@ import { exec as execCb, spawnSync } from 'child_process' import { createHash, randomUUID } from 'crypto' import { chmodSync, copyFileSync, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'fs' import { arch, homedir, platform } from 'os' -import { dirname, resolve, join } from 'path' +import { resolve, join } from 'path' import { Readable } from 'stream' import { finished } from 'stream/promises' import extractZip from 'extract-zip' -import { fileURLToPath } from 'url' -const __dirname = dirname(fileURLToPath(import.meta.url)) +const __dirname = import.meta.dirname const cwd = resolve(__dirname, '..') const PLAYWRIGHT_SKIP = process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === '1' || diff --git a/scripts/uninstall-pi-global.js b/scripts/uninstall-pi-global.js index a933bee63..ce347a282 100644 --- a/scripts/uninstall-pi-global.js +++ b/scripts/uninstall-pi-global.js @@ -1,10 +1,9 @@ #!/usr/bin/env node import { existsSync, readFileSync, readdirSync, rmSync, rmdirSync } from 'node:fs' import os from 'node:os' -import { dirname, join, resolve } from 'node:path' -import { fileURLToPath } from 'node:url' +import { join, resolve } from 'node:path' -const __dirname = dirname(fileURLToPath(import.meta.url)) +const __dirname = import.meta.dirname const resourcesDir = resolve(__dirname, '..', 'src', 'resources') const piRoot = join(os.homedir(), '.pi') const piAgentDir = join(piRoot, 'agent') diff --git a/scripts/watch-resources.js b/scripts/watch-resources.js index a2dd59985..df4940d77 100644 --- a/scripts/watch-resources.js +++ b/scripts/watch-resources.js @@ -14,10 +14,9 @@ import { watch } from 'node:fs' import { cpSync, mkdirSync, rmSync } from 'node:fs' -import { resolve, dirname } from 'node:path' -import { fileURLToPath } from 'node:url' +import { resolve } from 'node:path' -const __dirname = dirname(fileURLToPath(import.meta.url)) +const __dirname = import.meta.dirname const src = resolve(__dirname, '..', 'src', 'resources') const dest = resolve(__dirname, '..', 'dist', 'resources') diff --git a/sf-orchestrator/templates/spec.md b/sf-orchestrator/templates/spec.md index 441880f39..7aeedde9a 100644 --- a/sf-orchestrator/templates/spec.md +++ b/sf-orchestrator/templates/spec.md @@ -13,7 +13,7 @@ - Language: [Node.js / Python / Go / Rust / etc.] - Framework: [Express / FastAPI / none / etc.] - External dependencies: [list APIs, databases, services] -- Environment: [Node >= 22 / Python 3.12+ / etc.] +- Environment: [Node >= 24 / Python 3.12+ / etc.] ## Out of Scope - [Explicit exclusion 1 — prevents scope creep] diff --git a/src/cli.ts b/src/cli.ts index 245580a52..5c917b844 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -41,7 +41,7 @@ import { stopWebMode } from "./web-mode.js"; import { loadStoredEnvKeys } from "./wizard.js"; // --------------------------------------------------------------------------- -// V8 compile cache — Node 22+ can cache compiled bytecode across runs, +// V8 compile cache — Node 24+ can cache compiled bytecode across runs, // eliminating repeated parse/compile overhead for unchanged modules. // Must be set early so dynamic imports (extensions, lazy subcommands) benefit. // --------------------------------------------------------------------------- diff --git a/src/loader.ts b/src/loader.ts index a7990f4d7..1cf16daf2 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -6,15 +6,14 @@ import { readFileSync, symlinkSync, } from "node:fs"; -import { delimiter, dirname, join, relative, resolve } from "node:path"; +import { delimiter, join, relative, resolve } from "node:path"; // SF Startup Loader // Copyright (c) 2026 Singularity Forge -import { fileURLToPath } from "node:url"; // Fast-path: handle --version/-v and --help/-h before importing any heavy // dependencies. This avoids loading the entire pi-coding-agent barrel import // (~1s) just to print a version string. -const sfRootDir = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const sfRootDir = resolve(import.meta.dirname, ".."); const args = process.argv.slice(2); const firstArg = args[0]; @@ -46,7 +45,7 @@ if (firstArg === "--help" || firstArg === "-h") { // package.json (already parsed above) and verifies git is available. // --------------------------------------------------------------------------- { - const MIN_NODE_MAJOR = 22; + const MIN_NODE_MAJOR = 24; const red = "\x1b[31m"; const bold = "\x1b[1m"; const dim = "\x1b[2m"; @@ -96,7 +95,7 @@ import { applyRtkProcessEnv } from "./rtk.js"; // This allows config.js to: // 1. Read piConfig.name → "sf" (branding) // 2. Resolve themes via dist/ (no src/ present → uses dist path) -const pkgDir = resolve(dirname(fileURLToPath(import.meta.url)), "..", "pkg"); +const pkgDir = resolve(import.meta.dirname, "..", "pkg"); // MUST be set before any dynamic import of pi SDK fires — this is what config.js // reads to determine APP_NAME and CONFIG_DIR_NAME diff --git a/src/resource-loader.ts b/src/resource-loader.ts index f2e5b9366..62952818c 100644 --- a/src/resource-loader.ts +++ b/src/resource-loader.ts @@ -27,7 +27,6 @@ import { } from "node:fs"; import { homedir } from "node:os"; import { basename, dirname, join, relative, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; import { discoverExtensionEntryPaths } from "./extension-discovery.js"; import { ensureRegistryEntries, @@ -45,7 +44,7 @@ import { compareSemver } from "./update-check.js"; // that use sf — causing stale/broken extensions to be synced to ~/.sf/agent/. // dist/resources/ is populated by the build step (`npm run copy-resources`) and // reflects the built state, not the currently checked-out branch. -const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const packageRoot = resolve(import.meta.dirname, ".."); const distResources = join(packageRoot, "dist", "resources"); const srcResources = join(packageRoot, "src", "resources"); // Use dist/resources only if it has the full expected structure. diff --git a/src/resources/extensions/browser-tools/package.json b/src/resources/extensions/browser-tools/package.json index 8a717536c..6d6869ef1 100644 --- a/src/resources/extensions/browser-tools/package.json +++ b/src/resources/extensions/browser-tools/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "scripts": { "test": "node --test tests/*.test.mjs" }, diff --git a/src/resources/extensions/claude-code-cli/package.json b/src/resources/extensions/claude-code-cli/package.json index 47e9c93ed..dd35d104d 100644 --- a/src/resources/extensions/claude-code-cli/package.json +++ b/src/resources/extensions/claude-code-cli/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "pi": { "extensions": [ "./index.ts" diff --git a/src/resources/extensions/cmux/package.json b/src/resources/extensions/cmux/package.json index fca4d3c89..b82299376 100644 --- a/src/resources/extensions/cmux/package.json +++ b/src/resources/extensions/cmux/package.json @@ -3,5 +3,8 @@ "private": true, "type": "module", "description": "cmux integration library — used by other extensions, not an extension itself", + "engines": { + "node": ">=24.0.0" + }, "pi": {} } diff --git a/src/resources/extensions/context7/package.json b/src/resources/extensions/context7/package.json index 1022a915a..ec5c63fc1 100644 --- a/src/resources/extensions/context7/package.json +++ b/src/resources/extensions/context7/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "pi": { "extensions": [ "./index.ts" diff --git a/src/resources/extensions/genai-proxy/package.json b/src/resources/extensions/genai-proxy/package.json index 539c4a0f8..aa0b951fe 100644 --- a/src/resources/extensions/genai-proxy/package.json +++ b/src/resources/extensions/genai-proxy/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "pi": { "extensions": [ "./index.ts" diff --git a/src/resources/extensions/github-sync/sync.ts b/src/resources/extensions/github-sync/sync.ts index e6d52c50c..b1522bb7a 100644 --- a/src/resources/extensions/github-sync/sync.ts +++ b/src/resources/extensions/github-sync/sync.ts @@ -12,7 +12,7 @@ import { existsSync, readdirSync } from "node:fs"; import { join } from "node:path"; import { debugLog } from "../sf/debug-logger.js"; import { loadFile, parseSummary } from "../sf/files.js"; -import { parsePlan, parseRoadmap } from "../sf/parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "../sf/parsers.js"; import { resolveMilestoneFile, resolveSliceFile, diff --git a/src/resources/extensions/google-search/package.json b/src/resources/extensions/google-search/package.json index 6c056ea94..ef576ff93 100644 --- a/src/resources/extensions/google-search/package.json +++ b/src/resources/extensions/google-search/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "pi": { "extensions": [ "./index.ts" diff --git a/src/resources/extensions/package.json b/src/resources/extensions/package.json index bedb411a9..5711d93c7 100644 --- a/src/resources/extensions/package.json +++ b/src/resources/extensions/package.json @@ -1,3 +1,6 @@ { - "type": "module" + "type": "module", + "engines": { + "node": ">=24.0.0" + } } diff --git a/src/resources/extensions/sf-permissions/package.json b/src/resources/extensions/sf-permissions/package.json index a65d4e147..c7c369a37 100644 --- a/src/resources/extensions/sf-permissions/package.json +++ b/src/resources/extensions/sf-permissions/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "pi": { "extensions": [ "./index.ts" diff --git a/src/resources/extensions/sf/auto-direct-dispatch.ts b/src/resources/extensions/sf/auto-direct-dispatch.ts index b800b1922..f017d1b87 100644 --- a/src/resources/extensions/sf/auto-direct-dispatch.ts +++ b/src/resources/extensions/sf/auto-direct-dispatch.ts @@ -21,7 +21,7 @@ import { buildRunUatPrompt, } from "./auto-prompts.js"; import { loadFile } from "./files.js"; -import { parseRoadmap } from "./parsers-legacy.js"; +import { parseRoadmap } from "./parsers.js"; import { relSliceFile, resolveMilestoneFile, diff --git a/src/resources/extensions/sf/auto-dispatch.ts b/src/resources/extensions/sf/auto-dispatch.ts index fb5167789..a60dff077 100644 --- a/src/resources/extensions/sf/auto-dispatch.ts +++ b/src/resources/extensions/sf/auto-dispatch.ts @@ -44,7 +44,7 @@ import { resolveAllOverrides, } from "./files.js"; import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js"; -import { parseRoadmap } from "./parsers-legacy.js"; +import { parseRoadmap } from "./parsers.js"; import { buildMilestoneFileName, relSliceFile, diff --git a/src/resources/extensions/sf/auto-prompts.ts b/src/resources/extensions/sf/auto-prompts.ts index 47126e844..3fa015fb5 100644 --- a/src/resources/extensions/sf/auto-prompts.ts +++ b/src/resources/extensions/sf/auto-prompts.ts @@ -38,7 +38,7 @@ import { formatMemoriesForPrompt, getActiveMemoriesRanked, } from "./memory-store.js"; -import { parseRoadmap } from "./parsers-legacy.js"; +import { parseRoadmap } from "./parsers.js"; import { relMilestoneFile, relMilestonePath, diff --git a/src/resources/extensions/sf/auto-recovery.ts b/src/resources/extensions/sf/auto-recovery.ts index b8530d312..b2ed0a5dd 100644 --- a/src/resources/extensions/sf/auto-recovery.ts +++ b/src/resources/extensions/sf/auto-recovery.ts @@ -32,10 +32,7 @@ import { nativeMergeAbort, nativeResetHard, } from "./native-git-bridge.js"; -import { - parsePlan as parseLegacyPlan, - parseRoadmap as parseLegacyRoadmap, -} from "./parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "./parsers.js"; import { buildSliceFileName, clearPathCache, @@ -325,7 +322,7 @@ export function verifyExpectedArtifact( return false; } try { - const roadmap = parseLegacyRoadmap(readFileSync(roadmapFile, "utf-8")); + const roadmap = parseRoadmap(readFileSync(roadmapFile, "utf-8")); const milestoneResearchFile = resolveMilestoneFile(base, mid, "RESEARCH"); for (const slice of roadmap.slices) { if (slice.done) continue; @@ -367,7 +364,7 @@ export function verifyExpectedArtifact( try { const roadmapContent = readFileSync(absPath, "utf-8"); if (getMilestonePlanBlockingIssue(roadmapContent)) return false; - const roadmap = parseLegacyRoadmap(roadmapContent); + const roadmap = parseRoadmap(roadmapContent); if (roadmap.slices.length === 0) return false; } catch (err) { logWarning( @@ -441,7 +438,7 @@ export function verifyExpectedArtifact( if (!taskIds) { // LEGACY: DB unavailable or no tasks in DB — parse plan file for task IDs const planContent = readFileSync(absPath, "utf-8"); - const plan = parseLegacyPlan(planContent); + const plan = parsePlan(planContent); if (plan.tasks.length > 0) taskIds = plan.tasks.map((t: { id: string }) => t.id); } @@ -482,12 +479,12 @@ export function verifyExpectedArtifact( if (dbSlice.status !== "complete") return false; } else if (!isDbAvailable()) { // LEGACY: Pre-migration fallback for projects without DB. - // Fall back to roadmap checkbox check via parsers-legacy + // Fall back to roadmap checkbox check via parsers const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); if (roadmapFile && existsSync(roadmapFile)) { try { const roadmapContent = readFileSync(roadmapFile, "utf-8"); - const roadmap = parseLegacyRoadmap(roadmapContent); + const roadmap = parseRoadmap(roadmapContent); const slice = roadmap.slices.find((s) => s.id === sid); if (slice && !slice.done) return false; } catch (e) { diff --git a/src/resources/extensions/sf/auto-timeout-recovery.ts b/src/resources/extensions/sf/auto-timeout-recovery.ts index afa0c3d72..204037acb 100644 --- a/src/resources/extensions/sf/auto-timeout-recovery.ts +++ b/src/resources/extensions/sf/auto-timeout-recovery.ts @@ -17,7 +17,7 @@ import { verifyExpectedArtifact, writeBlockerPlaceholder, } from "./auto-recovery.js"; -import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js"; +import { parseRoadmap } from "./parsers.js"; import { relMilestoneFile, relSliceFile, @@ -93,7 +93,7 @@ export function inspectUnitRecoveryStatus( ); } else { try { - const roadmap = parseLegacyRoadmap( + const roadmap = parseRoadmap( readFileSync(roadmapPath, "utf-8"), ); const slice = roadmap.slices.find((s) => s.id === sid); diff --git a/src/resources/extensions/sf/auto-verification.ts b/src/resources/extensions/sf/auto-verification.ts index 6aedefa06..fc908e6f7 100644 --- a/src/resources/extensions/sf/auto-verification.ts +++ b/src/resources/extensions/sf/auto-verification.ts @@ -18,7 +18,7 @@ import type { } from "@singularity-forge/pi-coding-agent"; import type { AutoSession } from "./auto/session.js"; import { loadFile } from "./files.js"; -import { parseRoadmap } from "./parsers-legacy.js"; +import { parseRoadmap } from "./parsers.js"; import { resolveMilestoneFile, resolveSlicePath } from "./paths.js"; import { type PostExecutionResult, diff --git a/src/resources/extensions/sf/benchmark-selector.ts b/src/resources/extensions/sf/benchmark-selector.ts index 43905b20d..ddb93578b 100644 --- a/src/resources/extensions/sf/benchmark-selector.ts +++ b/src/resources/extensions/sf/benchmark-selector.ts @@ -22,8 +22,7 @@ */ import { existsSync, readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { tierOrdinal, type ComplexityTier } from "./complexity-classifier.js"; import { getModelTier } from "./model-router.js"; @@ -107,8 +106,7 @@ let _benchmarksCache: BenchmarkData | null = null; function loadBenchmarks(): BenchmarkData { if (_benchmarksCache) return _benchmarksCache; - const __filename = fileURLToPath(import.meta.url); - const here = dirname(__filename); + const here = import.meta.dirname; // Works for both .ts (dev) and .js (dist) since we copy the data file 1:1. const path = join(here, "learning", "data", "model-benchmarks.json"); if (!existsSync(path)) { diff --git a/src/resources/extensions/sf/commands-maintenance.ts b/src/resources/extensions/sf/commands-maintenance.ts index 391ede5f6..3065d8424 100644 --- a/src/resources/extensions/sf/commands-maintenance.ts +++ b/src/resources/extensions/sf/commands-maintenance.ts @@ -64,7 +64,7 @@ export async function handleCleanupBranches( const { listWorktrees } = await import("./worktree-manager.js"); const { resolveMilestoneFile } = await import("./paths.js"); const { loadFile } = await import("./files.js"); - const { parseRoadmap } = await import("./parsers-legacy.js"); + const { parseRoadmap } = await import("./parsers.js"); const { isMilestoneComplete } = await import("./state.js"); const { isDbAvailable, getMilestone } = await import("./sf-db.js"); diff --git a/src/resources/extensions/sf/commands-prefs-wizard.ts b/src/resources/extensions/sf/commands-prefs-wizard.ts index eeed5ccb8..90382506c 100644 --- a/src/resources/extensions/sf/commands-prefs-wizard.ts +++ b/src/resources/extensions/sf/commands-prefs-wizard.ts @@ -1094,7 +1094,7 @@ export async function ensurePreferencesFile( if (!existsSync(path)) { const template = await loadFile( join( - dirname(fileURLToPath(import.meta.url)), + import.meta.dirname, "templates", "PREFERENCES.md", ), diff --git a/src/resources/extensions/sf/dispatch-guard.ts b/src/resources/extensions/sf/dispatch-guard.ts index ec5754076..83ba0a6a9 100644 --- a/src/resources/extensions/sf/dispatch-guard.ts +++ b/src/resources/extensions/sf/dispatch-guard.ts @@ -2,7 +2,7 @@ import { readFileSync } from "node:fs"; import { findMilestoneIds } from "./guided-flow.js"; -import { parseRoadmap } from "./parsers-legacy.js"; +import { parseRoadmap } from "./parsers.js"; import { resolveMilestoneFile } from "./paths.js"; import { getMilestoneSlices, isDbAvailable } from "./sf-db.js"; import { isClosedStatus } from "./status-guards.js"; diff --git a/src/resources/extensions/sf/doctor-git-checks.ts b/src/resources/extensions/sf/doctor-git-checks.ts index f09170b98..52ed29a66 100644 --- a/src/resources/extensions/sf/doctor-git-checks.ts +++ b/src/resources/extensions/sf/doctor-git-checks.ts @@ -29,7 +29,7 @@ import { nativeWorktreeList, nativeWorktreeRemove, } from "./native-git-bridge.js"; -import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js"; +import { parseRoadmap } from "./parsers.js"; import { resolveMilestoneFile } from "./paths.js"; import { loadEffectiveSFPreferences } from "./preferences.js"; import { getMilestoneSlices, isDbAvailable } from "./sf-db.js"; @@ -116,7 +116,7 @@ export async function checkGitHealth( ? await loadFile(roadmapPath) : null; if (roadmapContent) { - const roadmap = parseLegacyRoadmap(roadmapContent); + const roadmap = parseRoadmap(roadmapContent); isComplete = isMilestoneComplete(roadmap); } } @@ -188,7 +188,7 @@ export async function checkGitHealth( ? await loadFile(roadmapPath) : null; if (!roadmapContent) continue; - const roadmap = parseLegacyRoadmap(roadmapContent); + const roadmap = parseRoadmap(roadmapContent); branchMilestoneComplete = isMilestoneComplete(roadmap); } if (branchMilestoneComplete) { diff --git a/src/resources/extensions/sf/doctor.ts b/src/resources/extensions/sf/doctor.ts index 36e20df9f..82999f77a 100644 --- a/src/resources/extensions/sf/doctor.ts +++ b/src/resources/extensions/sf/doctor.ts @@ -28,10 +28,7 @@ import { parseTaskPlanMustHaves, saveFile, } from "./files.js"; -import { - parsePlan as parseLegacyPlan, - parseRoadmap as parseLegacyRoadmap, -} from "./parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "./parsers.js"; import { milestonesDir, relMilestoneFile, @@ -354,7 +351,7 @@ export async function selectDoctorScope( dbSlices.length > 0 && dbSlices.every((s) => s.status === "complete"); if (!allDone) return milestone.id; } else { - const roadmap = parseLegacyRoadmap(roadmapContent); + const roadmap = parseRoadmap(roadmapContent); if (!isMilestoneComplete(roadmap)) return milestone.id; } } @@ -745,7 +742,7 @@ export async function runSFDoctor( } else { const activeMilestoneId = state.activeMilestone?.id; const activeSliceId = state.activeSlice?.id; - slices = parseLegacyRoadmap(roadmapContent).slices.map((s) => ({ + slices = parseRoadmap(roadmapContent).slices.map((s) => ({ ...s, // Legacy roadmaps only encode done vs not-done. For doctor's // missing-directory checks, treat every undone slice except the @@ -896,7 +893,7 @@ export async function runSFDoctor( "PLAN", ); const planContent = planPath ? await loadFile(planPath) : null; - // Normalize plan tasks: prefer DB, fall back to parsers-legacy + // Normalize plan tasks: prefer DB, fall back to parsers let plan: { tasks: Array<{ id: string; @@ -919,7 +916,7 @@ export async function runSFDoctor( } } if (!plan && planContent) { - plan = parseLegacyPlan(planContent); + plan = parsePlan(planContent); } if (!plan) { if (!slice.done) { diff --git a/src/resources/extensions/sf/files.ts b/src/resources/extensions/sf/files.ts index 8488dd63a..22bd6c6c5 100644 --- a/src/resources/extensions/sf/files.ts +++ b/src/resources/extensions/sf/files.ts @@ -16,7 +16,6 @@ import { findMilestoneIds } from "./milestone-ids.js"; import { NATIVE_UNAVAILABLE, nativeExtractSection, - nativeParseSummaryFile, } from "./native-parser-bridge.js"; import { relMilestoneFile, @@ -72,18 +71,18 @@ function cachedParse( } // ─── Cross-module cache clear registry ──────────────────────────────────── -// parsers-legacy.ts registers its cache-clear callback here at module init +// parsers.ts registers its cache-clear callback here at module init // to avoid circular imports. clearParseCache() calls all registered callbacks. const _cacheClearCallbacks: (() => void)[] = []; /** Register a callback to be invoked when clearParseCache() is called. - * Used by parsers-legacy.ts to synchronously clear its own cache. */ + * Used by parsers.ts to synchronously clear its own cache. */ export function registerCacheClearCallback(cb: () => void): void { _cacheClearCallbacks.push(cb); } /** Clear the module-scoped parse cache. Call when files change on disk. - * Also clears any registered external caches (e.g. parsers-legacy.ts). */ + * Also clears any registered external caches (e.g. parsers.ts). */ export function clearParseCache(): void { _parseCache.clear(); for (const cb of _cacheClearCallbacks) cb(); @@ -330,38 +329,6 @@ export function parseSummary(content: string): Summary { } function _parseSummaryImpl(content: string): Summary { - // Try native parser first for better performance - const nativeResult = nativeParseSummaryFile(content); - if (nativeResult) { - const nfm = nativeResult.frontmatter; - return { - frontmatter: { - id: nfm.id, - parent: nfm.parent, - milestone: nfm.milestone, - provides: nfm.provides, - requires: nfm.requires, - affects: nfm.affects, - key_files: nfm.keyFiles, - key_decisions: nfm.keyDecisions, - patterns_established: nfm.patternsEstablished, - drill_down_paths: nfm.drillDownPaths, - observability_surfaces: nfm.observabilitySurfaces, - duration: nfm.duration, - verification_result: nfm.verificationResult, - completed_at: nfm.completedAt, - blocker_discovered: nfm.blockerDiscovered, - }, - title: nativeResult.title, - oneLiner: nativeResult.oneLiner, - whatHappened: nativeResult.whatHappened, - deviations: nativeResult.deviations, - filesModified: nativeResult.filesModified, - followUps: extractSection(content, "Follow-ups") ?? "", - knownLimitations: extractSection(content, "Known Limitations") ?? "", - }; - } - const [fmLines, body] = splitFrontmatter(content); const fm = fmLines ? parseFrontmatterMap(fmLines) : {}; diff --git a/src/resources/extensions/sf/forensics.ts b/src/resources/extensions/sf/forensics.ts index 28609ff80..a8a9aa99a 100644 --- a/src/resources/extensions/sf/forensics.ts +++ b/src/resources/extensions/sf/forensics.ts @@ -17,8 +17,7 @@ import { writeFileSync, } from "node:fs"; import { homedir } from "node:os"; -import { dirname, join, relative } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join, relative } from "node:path"; import type { ExtensionAPI, ExtensionCommandContext, @@ -338,7 +337,7 @@ export async function handleForensics( // Derive SF source dir for prompt — fall back to ~/.sf/agent/extensions/sf/ // when import.meta.url resolves to the npm-global install path (Windows). - let sfSourceDir = dirname(fileURLToPath(import.meta.url)); + let sfSourceDir = import.meta.dirname; if (!existsSync(join(sfSourceDir, "prompts"))) { const sfHome = process.env.SF_HOME || join(homedir(), ".sf"); const fallback = join(sfHome, "agent", "extensions", "sf"); diff --git a/src/resources/extensions/sf/learning/index.mjs b/src/resources/extensions/sf/learning/index.mjs index 8226fee27..125ec1ad2 100644 --- a/src/resources/extensions/sf/learning/index.mjs +++ b/src/resources/extensions/sf/learning/index.mjs @@ -41,8 +41,7 @@ import { readFileSync } from "node:fs"; import { homedir } from "node:os"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { resolve } from "node:path"; import { writeFallbackChains } from "./fallback-chain-writer.mjs"; import { createBeforeModelSelectHandler, @@ -52,7 +51,7 @@ import { loadCapabilityOverrides } from "./loadCapabilityOverrides.mjs"; import { aggregateAllForUnitType } from "./outcome-aggregator.mjs"; import { ensureSchema, recordOutcome } from "./outcome-recorder.mjs"; -const MODULE_DIRECTORY = dirname(fileURLToPath(import.meta.url)); +const MODULE_DIRECTORY = import.meta.dirname; const SCHEMA_PATH = resolve(MODULE_DIRECTORY, "outcome-schema.sql"); const DEFAULT_DB_PATH = "~/.sf/sf-learning.db"; const DEFAULT_N_PRIOR = 10; diff --git a/src/resources/extensions/sf/learning/loadCapabilityOverrides.mjs b/src/resources/extensions/sf/learning/loadCapabilityOverrides.mjs index f87659f7d..a765f9248 100644 --- a/src/resources/extensions/sf/learning/loadCapabilityOverrides.mjs +++ b/src/resources/extensions/sf/learning/loadCapabilityOverrides.mjs @@ -42,10 +42,9 @@ */ import { readFile } from "node:fs/promises"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { resolve } from "node:path"; -const MODULE_DIRECTORY = dirname(fileURLToPath(import.meta.url)); +const MODULE_DIRECTORY = import.meta.dirname; const DEFAULT_BENCHMARKS_PATH = resolve( MODULE_DIRECTORY, "data/model-benchmarks.json", diff --git a/src/resources/extensions/sf/markdown-renderer.ts b/src/resources/extensions/sf/markdown-renderer.ts index ce0e5dac0..5a6e73696 100644 --- a/src/resources/extensions/sf/markdown-renderer.ts +++ b/src/resources/extensions/sf/markdown-renderer.ts @@ -9,7 +9,6 @@ // parseRoadmap(), parsePlan(), parseSummary() in files.ts. import { existsSync, mkdirSync, readFileSync } from "node:fs"; -import { createRequire } from "node:module"; import { join, relative } from "node:path"; import { clearParseCache, saveFile } from "./files.js"; import { @@ -38,6 +37,8 @@ import { invalidateStateCache } from "./state.js"; import { isClosedStatus } from "./status-guards.js"; import type { GateRow } from "./types.js"; import { logWarning } from "./workflow-logger.js"; +import { parseRoadmap, parsePlan } from "./parsers.js"; +const parsers = { parseRoadmap, parsePlan }; // ─── Helpers ────────────────────────────────────────────────────────────── @@ -1019,22 +1020,10 @@ export interface StaleEntry { * Logs to stderr when stale files are detected. */ export function detectStaleRenders(basePath: string): StaleEntry[] { - // Lazy-load parsers — intentional disk-vs-DB comparison requires parsers - const _require = createRequire(import.meta.url); - let parseRoadmap: (...args: any[]) => any, parsePlan: (...args: any[]) => any; - try { - const m = _require("./parsers-legacy.ts"); - parseRoadmap = m.parseRoadmap; - parsePlan = m.parsePlan; - } catch (e) { - logWarning( - "renderer", - `parsers-legacy.ts require failed, falling back to .js: ${(e as Error).message}`, - ); - const m = _require("./parsers-legacy.js"); - parseRoadmap = m.parseRoadmap; - parsePlan = m.parsePlan; - } + // Parsers are statically imported at module level; they were previously + // lazy-loaded via require() but vitest/Vite doesn't resolve .ts through + // Node's require() pipeline. + const { parseRoadmap, parsePlan } = parsers; const stale: StaleEntry[] = []; const milestones = getAllMilestones(); diff --git a/src/resources/extensions/sf/md-importer.ts b/src/resources/extensions/sf/md-importer.ts index e79974a99..b56894621 100644 --- a/src/resources/extensions/sf/md-importer.ts +++ b/src/resources/extensions/sf/md-importer.ts @@ -8,7 +8,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { parseContextDependsOn } from "./files.js"; import { findMilestoneIds } from "./guided-flow.js"; -import { parsePlan, parseRoadmap } from "./parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "./parsers.js"; import { milestonesDir, resolveMilestoneFile, diff --git a/src/resources/extensions/sf/migrate/command.ts b/src/resources/extensions/sf/migrate/command.ts index ddc2ba040..2445d1a42 100644 --- a/src/resources/extensions/sf/migrate/command.ts +++ b/src/resources/extensions/sf/migrate/command.ts @@ -10,8 +10,7 @@ */ import { existsSync, readFileSync } from "node:fs"; -import { dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join, resolve } from "node:path"; import type { ExtensionAPI, ExtensionCommandContext, @@ -50,7 +49,7 @@ function buildReviewPrompt( preview: MigrationPreview, ): string { const promptsDir = join( - dirname(fileURLToPath(import.meta.url)), + import.meta.dirname, "..", "prompts", ); diff --git a/src/resources/extensions/sf/package.json b/src/resources/extensions/sf/package.json index 91f48658d..ba5eca5ad 100644 --- a/src/resources/extensions/sf/package.json +++ b/src/resources/extensions/sf/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "pi": { "extensions": [ "./index.ts" diff --git a/src/resources/extensions/sf/parallel-merge.ts b/src/resources/extensions/sf/parallel-merge.ts index 45afdb6ae..1a23487cd 100644 --- a/src/resources/extensions/sf/parallel-merge.ts +++ b/src/resources/extensions/sf/parallel-merge.ts @@ -34,7 +34,7 @@ export type MergeOrder = "sequential" | "by-completion"; /** * Check whether a milestone is complete by querying its worktree SQLite DB. - * Uses a subprocess to avoid disrupting the global DB singleton. + * Uses Node's built-in SQLite provider to avoid disrupting the global DB singleton. * Returns true when milestones.status = 'complete' in the worktree's sf.db. */ /** diff --git a/src/resources/extensions/sf/parallel-orchestrator.ts b/src/resources/extensions/sf/parallel-orchestrator.ts index cd07e671c..768a4e588 100644 --- a/src/resources/extensions/sf/parallel-orchestrator.ts +++ b/src/resources/extensions/sf/parallel-orchestrator.ts @@ -826,7 +826,7 @@ function resolveSfBin(): string | null { // loader.js is at dist/loader.js let thisDir: string; try { - thisDir = dirname(fileURLToPath(import.meta.url)); + thisDir = import.meta.dirname; } catch (e) { logWarning( "parallel", @@ -1120,9 +1120,11 @@ export function refreshWorkerStatuses( // If all workers are in a terminal state (error/stopped), the orchestration // is finished — deactivate and clean up so zombie workers don't persist. - const allDead = [...state.workers.values()].every( - (w) => w.state === "error" || w.state === "stopped", - ); + const allDead = + state.workers.size > 0 && + [...state.workers.values()].every( + (w) => w.state === "error" || w.state === "stopped", + ); if (allDead) { state.active = false; removeStateFile(basePath); diff --git a/src/resources/extensions/sf/parsers-legacy.ts b/src/resources/extensions/sf/parsers.ts similarity index 89% rename from src/resources/extensions/sf/parsers-legacy.ts rename to src/resources/extensions/sf/parsers.ts index d0c2fb69b..d73847f29 100644 --- a/src/resources/extensions/sf/parsers-legacy.ts +++ b/src/resources/extensions/sf/parsers.ts @@ -1,4 +1,4 @@ -// SF Extension - Legacy Parsers +// SF Extension - Parsers // parseRoadmap() and parsePlan() extracted from files.ts. // Used only by: md-importer.ts (migration), state.ts (pre-migration fallback), // markdown-renderer.ts (detectStaleRenders disk-vs-DB comparison), @@ -17,10 +17,9 @@ import { registerCacheClearCallback, } from "./files.js"; import { - nativeParsePlanFile, nativeParseRoadmap, } from "./native-parser-bridge.js"; -// Re-export parseRoadmapSlices so callers can import all legacy parsers from one module +// Re-export parseRoadmapSlices so callers can import all parsers from one module import { parseRoadmapSlices } from "./roadmap-slices.js"; import type { BoundaryMapEntry, @@ -60,13 +59,13 @@ function cachedParse( return result; } -/** Clear the legacy parser cache. Called by clearParseCache() in files.ts. */ -export function clearLegacyParseCache(): void { +/** Clear the parser cache. Called by clearParseCache() in files.ts. */ +export function clearParserCache(): void { _parseCache.clear(); } // Register with files.ts so clearParseCache() also clears our cache -registerCacheClearCallback(clearLegacyParseCache); +registerCacheClearCallback(clearParserCache); // ─── Roadmap Parser ──────────────────────────────────────────────────────── @@ -76,7 +75,7 @@ export function parseRoadmap(content: string): Roadmap { function _parseRoadmapImpl(content: string): Roadmap { const stopTimer = debugTime("parse-roadmap"); - // Try native parser first for better performance. Fall back to legacy if + // Try native parser first for better performance. Fall back to parser if // native finds zero slices (e.g. table-style roadmaps not yet supported). const nativeResult = nativeParseRoadmap(content); if (nativeResult && nativeResult.slices.length > 0) { @@ -179,30 +178,6 @@ export function parsePlan(content: string): SlicePlan { function _parsePlanImpl(content: string): SlicePlan { const stopTimer = debugTime("parse-plan"); const [, body] = splitFrontmatter(content); - // Try native parser first for better performance. Fall back to legacy if - // native finds zero tasks (e.g. heading-style tasks not yet supported). - const nativeResult = nativeParsePlanFile(body); - if (nativeResult && nativeResult.tasks.length > 0) { - stopTimer({ native: true }); - return { - id: nativeResult.id, - title: nativeResult.title, - goal: nativeResult.goal, - demo: nativeResult.demo, - mustHaves: nativeResult.mustHaves, - tasks: nativeResult.tasks.map((t) => ({ - id: t.id, - title: t.title, - description: t.description, - done: t.done, - estimate: t.estimate, - ...(t.files.length > 0 ? { files: t.files } : {}), - ...(t.verify ? { verify: t.verify } : {}), - })), - filesLikelyTouched: nativeResult.filesLikelyTouched, - }; - } - const lines = body.split("\n"); const h1 = lines.find((l) => l.startsWith("# ")); diff --git a/src/resources/extensions/sf/prompt-loader.ts b/src/resources/extensions/sf/prompt-loader.ts index 8eca6a248..f7f5c57e2 100644 --- a/src/resources/extensions/sf/prompt-loader.ts +++ b/src/resources/extensions/sf/prompt-loader.ts @@ -19,8 +19,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs"; import { homedir } from "node:os"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { SF_PARSE_ERROR, SFError } from "./errors.js"; import { logWarning } from "./workflow-logger.js"; @@ -35,7 +34,7 @@ import { logWarning } from "./workflow-logger.js"; * the user-local agent directory. */ function resolveExtensionDir(): string { - const moduleDir = dirname(fileURLToPath(import.meta.url)); + const moduleDir = import.meta.dirname; if (existsSync(join(moduleDir, "prompts"))) return moduleDir; // Fallback: user-local agent directory diff --git a/src/resources/extensions/sf/reactive-graph.ts b/src/resources/extensions/sf/reactive-graph.ts index 977d884b0..2454125d8 100644 --- a/src/resources/extensions/sf/reactive-graph.ts +++ b/src/resources/extensions/sf/reactive-graph.ts @@ -13,7 +13,7 @@ import { existsSync, unlinkSync } from "node:fs"; import { join } from "node:path"; import { loadFile, parseTaskPlanIO } from "./files.js"; import { loadJsonFileOrNull, saveJsonFile } from "./json-persistence.js"; -import { parsePlan } from "./parsers-legacy.js"; +import { parsePlan } from "./parsers.js"; import { resolveTaskFiles, resolveTasksDir } from "./paths.js"; import { getSliceTasks, isDbAvailable } from "./sf-db.js"; import type { diff --git a/src/resources/extensions/sf/slice-parallel-orchestrator.ts b/src/resources/extensions/sf/slice-parallel-orchestrator.ts index 54758869f..dd01da398 100644 --- a/src/resources/extensions/sf/slice-parallel-orchestrator.ts +++ b/src/resources/extensions/sf/slice-parallel-orchestrator.ts @@ -15,8 +15,7 @@ import { type ChildProcess, spawn } from "node:child_process"; import { appendFileSync, existsSync, mkdirSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import { runWorktreePostCreateHook } from "./auto-worktree.js"; import { getErrorMessage } from "./error-utils.js"; import { sfRoot } from "./paths.js"; @@ -340,7 +339,7 @@ function resolveSfBin(): string | null { let thisDir: string; try { - thisDir = dirname(fileURLToPath(import.meta.url)); + thisDir = import.meta.dirname; } catch { thisDir = process.cwd(); } diff --git a/src/resources/extensions/sf/state.ts b/src/resources/extensions/sf/state.ts index 2f7761b61..34e4c692c 100644 --- a/src/resources/extensions/sf/state.ts +++ b/src/resources/extensions/sf/state.ts @@ -16,7 +16,7 @@ import { findMilestoneIds } from "./milestone-ids.js"; import { getVisionAlignmentBlockingIssue } from "./milestone-quality.js"; import { isTerminalMilestoneSummaryContent } from "./milestone-summary-classifier.js"; import { nativeBatchParseSfFiles } from "./native-parser-bridge.js"; -import { parsePlan, parseRoadmap } from "./parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "./parsers.js"; import { clearPathCache, resolveMilestoneFile, diff --git a/src/resources/extensions/sf/tests/auto-recovery.test.ts b/src/resources/extensions/sf/tests/auto-recovery.test.ts index d849a651d..c0de92927 100644 --- a/src/resources/extensions/sf/tests/auto-recovery.test.ts +++ b/src/resources/extensions/sf/tests/auto-recovery.test.ts @@ -14,7 +14,7 @@ import { } from "../auto-recovery.ts"; import { invalidateAllCaches } from "../cache.ts"; import { clearParseCache } from "../files.ts"; -import { parseRoadmap } from "../parsers-legacy.ts"; +import { parseRoadmap } from "../parsers.ts"; import { closeDatabase, insertGateRow, diff --git a/src/resources/extensions/sf/tests/complete-milestone.test.ts b/src/resources/extensions/sf/tests/complete-milestone.test.ts index 5f86c7ad2..ee4e21497 100644 --- a/src/resources/extensions/sf/tests/complete-milestone.test.ts +++ b/src/resources/extensions/sf/tests/complete-milestone.test.ts @@ -627,7 +627,7 @@ describe("complete-milestone", () => { const { invalidateAllCaches: invalidateAllCachesDynamic } = await import( "../cache.ts" ); - const { parseRoadmap } = await import("../parsers-legacy.ts"); + const { parseRoadmap } = await import("../parsers.ts"); const base = createFixtureBase(); try { diff --git a/src/resources/extensions/sf/tests/integration/auto-recovery.test.ts b/src/resources/extensions/sf/tests/integration/auto-recovery.test.ts index b078789c8..a27efd395 100644 --- a/src/resources/extensions/sf/tests/integration/auto-recovery.test.ts +++ b/src/resources/extensions/sf/tests/integration/auto-recovery.test.ts @@ -24,7 +24,7 @@ import { import { invalidateAllCaches } from "../../cache.ts"; import { clearParseCache, parseTaskPlanFile } from "../../files.ts"; import { renderPlanFromDb } from "../../markdown-renderer.ts"; -import { parsePlan, parseRoadmap } from "../../parsers-legacy.ts"; +import { parsePlan, parseRoadmap } from "../../parsers.ts"; import { closeDatabase, insertMilestone, diff --git a/src/resources/extensions/sf/tests/integration/doctor-environment.test.ts b/src/resources/extensions/sf/tests/integration/doctor-environment.test.ts index 3a5286a4f..008928827 100644 --- a/src/resources/extensions/sf/tests/integration/doctor-environment.test.ts +++ b/src/resources/extensions/sf/tests/integration/doctor-environment.test.ts @@ -389,7 +389,7 @@ describe("doctor-environment", async () => { test("env: docker check with Dockerfile", () => { const dir = createProjectDir({ "package.json": JSON.stringify({ name: "test" }), - Dockerfile: "FROM node:22\n", + Dockerfile: "FROM node:24\n", }); mkdirSync(join(dir, "node_modules"), { recursive: true }); cleanups.push(dir); @@ -405,7 +405,7 @@ describe("doctor-environment", async () => { // ── Doctor Issue Conversion ──────────────────────────────────────── test("env: converts results to doctor issues", () => { const results: EnvironmentCheckResult[] = [ - { name: "node_version", status: "ok", message: "Node.js v22.0.0" }, + { name: "node_version", status: "ok", message: "Node.js v24.0.0" }, { name: "dependencies", status: "error", @@ -461,7 +461,7 @@ describe("doctor-environment", async () => { // ── Report Formatting ────────────────────────────────────────────── test("env: formatEnvironmentReport", () => { const results: EnvironmentCheckResult[] = [ - { name: "node_version", status: "ok", message: "Node.js v22.0.0" }, + { name: "node_version", status: "ok", message: "Node.js v24.0.0" }, { name: "dependencies", status: "error", @@ -473,7 +473,7 @@ describe("doctor-environment", async () => { const report = formatEnvironmentReport(results); assert.ok(report.includes("Environment Health:"), "has header"); - assert.ok(report.includes("Node.js v22.0.0"), "includes ok result"); + assert.ok(report.includes("Node.js v24.0.0"), "includes ok result"); assert.ok( report.includes("node_modules missing"), "includes error result", diff --git a/src/resources/extensions/sf/tests/integration/doctor-false-positives.test.ts b/src/resources/extensions/sf/tests/integration/doctor-false-positives.test.ts index 16593e84d..84db1ea3f 100644 --- a/src/resources/extensions/sf/tests/integration/doctor-false-positives.test.ts +++ b/src/resources/extensions/sf/tests/integration/doctor-false-positives.test.ts @@ -5,7 +5,7 @@ import { join } from "node:path"; import { describe, test } from 'vitest'; import { runSFDoctor } from "../../doctor.js"; -import { parsePlan } from "../../parsers-legacy.js"; +import { parsePlan } from "../../parsers.js"; // ── Helpers ───────────────────────────────────────────────────────────────── diff --git a/src/resources/extensions/sf/tests/integration/doctor-fixlevel.test.ts b/src/resources/extensions/sf/tests/integration/doctor-fixlevel.test.ts index 2fe5c3495..4b2233275 100644 --- a/src/resources/extensions/sf/tests/integration/doctor-fixlevel.test.ts +++ b/src/resources/extensions/sf/tests/integration/doctor-fixlevel.test.ts @@ -170,7 +170,7 @@ test("legacy roadmap fallback: future slices are treated as pending, active slic rmSync(tmp, { recursive: true, force: true }); }); - // Force the legacy parser branch. + // Force the parser branch. try { closeDatabase(); } catch { diff --git a/src/resources/extensions/sf/tests/integration/doctor-runtime.test.ts b/src/resources/extensions/sf/tests/integration/doctor-runtime.test.ts index 1a896c1f7..3c8770d37 100644 --- a/src/resources/extensions/sf/tests/integration/doctor-runtime.test.ts +++ b/src/resources/extensions/sf/tests/integration/doctor-runtime.test.ts @@ -344,8 +344,8 @@ None ); assert.doesNotThrow( - () => run("git check-ignore -q .sf", dir), - "git now ignores .sf after fix", + () => run("git check-ignore -q .sf-id", dir), + "git now ignores SF runtime patterns after fix", ); }); } else { diff --git a/src/resources/extensions/sf/tests/integration/gitignore-tracked-sf.test.ts b/src/resources/extensions/sf/tests/integration/gitignore-tracked-sf.test.ts index b63801404..d65e6fe13 100644 --- a/src/resources/extensions/sf/tests/integration/gitignore-tracked-sf.test.ts +++ b/src/resources/extensions/sf/tests/integration/gitignore-tracked-sf.test.ts @@ -125,7 +125,7 @@ test("ensureGitignore does NOT add .sf when .sf/ has tracked files (#1364)", (_t } }); -test("ensureGitignore excludes .sf when .sf/ has NO tracked files", (_t) => { +test("ensureGitignore excludes SF runtime patterns when .sf/ has NO tracked files", (_t) => { const dir = makeTempRepo(); try { // Run ensureGitignore (no .sf/ at all) @@ -133,11 +133,13 @@ test("ensureGitignore excludes .sf when .sf/ has NO tracked files", (_t) => { const exclude = readFileSync(join(dir, ".git", "info", "exclude"), "utf-8"); const lines = exclude.split("\n").map((l) => l.trim()); + // Per ADR-001, local directories use granular runtime exclusions, + // not the blanket .sf pattern (which is reserved for symlink mode). assert.ok( - lines.includes(".sf"), - `Expected .sf in .git/info/exclude, but it's missing:\n${exclude}`, + lines.includes(".sf-id"), + `Expected .sf-id in .git/info/exclude, but it's missing:\n${exclude}`, ); - assert.doesNotThrow(() => git(dir, "check-ignore", "-q", ".sf")); + assert.doesNotThrow(() => git(dir, "check-ignore", "-q", ".sf-id")); } finally { cleanup(dir); } diff --git a/src/resources/extensions/sf/tests/markdown-renderer.test.ts b/src/resources/extensions/sf/tests/markdown-renderer.test.ts index c47913e2c..a145e9bbe 100644 --- a/src/resources/extensions/sf/tests/markdown-renderer.test.ts +++ b/src/resources/extensions/sf/tests/markdown-renderer.test.ts @@ -15,7 +15,7 @@ import { renderTaskSummary, repairStaleRenders, } from "../markdown-renderer.ts"; -import { parsePlan, parseRoadmap } from "../parsers-legacy.ts"; +import { parsePlan, parseRoadmap } from "../parsers.ts"; import { _clearSfRootCache, clearPathCache } from "../paths.ts"; import { _getAdapter, diff --git a/src/resources/extensions/sf/tests/migrate-writer-integration.test.ts b/src/resources/extensions/sf/tests/migrate-writer-integration.test.ts index 914b1d3e6..7df1f2799 100644 --- a/src/resources/extensions/sf/tests/migrate-writer-integration.test.ts +++ b/src/resources/extensions/sf/tests/migrate-writer-integration.test.ts @@ -19,7 +19,7 @@ import type { SFTask, } from "../migrate/types.ts"; import { writeSFDirectory } from "../migrate/writer.ts"; -import { parsePlan, parseRoadmap } from "../parsers-legacy.ts"; +import { parsePlan, parseRoadmap } from "../parsers.ts"; import { deriveState } from "../state.ts"; // ─── Fixture Builders ────────────────────────────────────────────────────── diff --git a/src/resources/extensions/sf/tests/migrate-writer.test.ts b/src/resources/extensions/sf/tests/migrate-writer.test.ts index 79fda3a9b..468d83a33 100644 --- a/src/resources/extensions/sf/tests/migrate-writer.test.ts +++ b/src/resources/extensions/sf/tests/migrate-writer.test.ts @@ -25,7 +25,7 @@ import { formatState, formatTaskSummary, } from "../migrate/writer.ts"; -import { parsePlan, parseRoadmap } from "../parsers-legacy.ts"; +import { parsePlan, parseRoadmap } from "../parsers.ts"; // ─── Test Data Builders ──────────────────────────────────────────────────── diff --git a/src/resources/extensions/sf/tests/parsers.test.ts b/src/resources/extensions/sf/tests/parsers.test.ts index cdd58e94e..e18d95f4f 100644 --- a/src/resources/extensions/sf/tests/parsers.test.ts +++ b/src/resources/extensions/sf/tests/parsers.test.ts @@ -8,7 +8,7 @@ import { parseSummary, parseTaskPlanFile, } from "../files.ts"; -import { parsePlan, parseRoadmap } from "../parsers-legacy.ts"; +import { parsePlan, parseRoadmap } from "../parsers.ts"; // ═══════════════════════════════════════════════════════════════════════════ // parseRoadmap tests @@ -1639,17 +1639,12 @@ Do things. `; const c = parseContinue(content); - // parseInt("abc") returns NaN; the parser || 0 fallback should give 0 - // Actually, looking at parser: typeof fm.step === 'string' ? parseInt(fm.step) : ... - // parseInt("abc") = NaN, and NaN || 0 doesn't work because NaN is falsy only in boolean context - // But the parser uses: typeof fm.step === 'string' ? parseInt(fm.step) : (fm.step as number) || 0 - // parseInt returns NaN which is a number, not 0 — let's verify - const stepIsNaN = Number.isNaN(c.frontmatter.step); - const totalIsNaN = Number.isNaN(c.frontmatter.totalSteps); - // The parser does parseInt which returns NaN for non-numeric strings - // There's no || 0 fallback on the parseInt path, so NaN is expected - assert.ok(stepIsNaN, "NaN step when non-numeric string"); - assert.ok(totalIsNaN, "NaN totalSteps when non-numeric string"); + assert.deepStrictEqual(c.frontmatter.step, 0, "invalid step defaults to 0"); + assert.deepStrictEqual( + c.frontmatter.totalSteps, + 0, + "invalid totalSteps defaults to 0", + ); }); test("parseContinue: all three status variants", () => { @@ -1766,7 +1761,7 @@ Decided to use approach A over approach B because of performance. ## Context -Running in worktree. Node 22 required. TypeScript strict mode. +Running in worktree. Node 24 required. TypeScript strict mode. ## Next Action @@ -1790,7 +1785,7 @@ Pick up at step 3: run the integration tests. c.decisions.includes("approach A over approach B"), "decisions detail", ); - assert.ok(c.context.includes("Node 22 required"), "context detail"); + assert.ok(c.context.includes("Node 24 required"), "context detail"); assert.ok( c.nextAction.includes("step 3: run the integration tests"), "nextAction detail", diff --git a/src/resources/extensions/sf/tests/plan-milestone.test.ts b/src/resources/extensions/sf/tests/plan-milestone.test.ts index 4c8feb5c0..e1fa0a6da 100644 --- a/src/resources/extensions/sf/tests/plan-milestone.test.ts +++ b/src/resources/extensions/sf/tests/plan-milestone.test.ts @@ -10,7 +10,7 @@ import { import { tmpdir } from "node:os"; import { join } from "node:path"; import { test } from 'vitest'; -import { parseRoadmap } from "../parsers-legacy.ts"; +import { parseRoadmap } from "../parsers.ts"; import { closeDatabase, getMilestone, diff --git a/src/resources/extensions/sf/tests/plan-slice.test.ts b/src/resources/extensions/sf/tests/plan-slice.test.ts index ff7051dc2..fde7bb99a 100644 --- a/src/resources/extensions/sf/tests/plan-slice.test.ts +++ b/src/resources/extensions/sf/tests/plan-slice.test.ts @@ -11,7 +11,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { test } from 'vitest'; import { parseTaskPlanFile } from "../files.ts"; -import { parsePlan } from "../parsers-legacy.ts"; +import { parsePlan } from "../parsers.ts"; import { closeDatabase, getSlice, diff --git a/src/resources/extensions/sf/tests/planning-crossval.test.ts b/src/resources/extensions/sf/tests/planning-crossval.test.ts index 91950d5c1..30d3e28c3 100644 --- a/src/resources/extensions/sf/tests/planning-crossval.test.ts +++ b/src/resources/extensions/sf/tests/planning-crossval.test.ts @@ -7,7 +7,7 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { renderPlanFromDb, renderRoadmapFromDb } from "../markdown-renderer.ts"; -import { parsePlan } from "../parsers-legacy.ts"; +import { parsePlan } from "../parsers.ts"; import { parseRoadmapSlices } from "../roadmap-slices.ts"; import { closeDatabase, diff --git a/src/resources/extensions/sf/tests/replan-handler.test.ts b/src/resources/extensions/sf/tests/replan-handler.test.ts index 95b231b93..715dae046 100644 --- a/src/resources/extensions/sf/tests/replan-handler.test.ts +++ b/src/resources/extensions/sf/tests/replan-handler.test.ts @@ -9,7 +9,7 @@ import { import { tmpdir } from "node:os"; import { join } from "node:path"; import { test } from 'vitest'; -import { parsePlan } from "../parsers-legacy.ts"; +import { parsePlan } from "../parsers.ts"; import { _getAdapter, closeDatabase, diff --git a/src/resources/extensions/sf/tests/roadmap-slices.test.ts b/src/resources/extensions/sf/tests/roadmap-slices.test.ts index 34cd543d0..5ac3080fb 100644 --- a/src/resources/extensions/sf/tests/roadmap-slices.test.ts +++ b/src/resources/extensions/sf/tests/roadmap-slices.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { test } from 'vitest'; -import { parseRoadmap } from "../parsers-legacy.ts"; +import { parseRoadmap } from "../parsers.ts"; import { expandDependencies, parseRoadmapSlices } from "../roadmap-slices.ts"; const content = `# M003: Current diff --git a/src/resources/extensions/sf/visualizer-data.ts b/src/resources/extensions/sf/visualizer-data.ts index c455dc043..66adcf264 100644 --- a/src/resources/extensions/sf/visualizer-data.ts +++ b/src/resources/extensions/sf/visualizer-data.ts @@ -33,7 +33,7 @@ import { loadLedgerFromDisk, } from "./metrics.js"; import { findMilestoneIds } from "./milestone-ids.js"; -import { parsePlan, parseRoadmap } from "./parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "./parsers.js"; import { resolveMilestoneFile, resolveSfRootFile, diff --git a/src/resources/extensions/sf/workflow-migration.ts b/src/resources/extensions/sf/workflow-migration.ts index 3fa639415..8fa412937 100644 --- a/src/resources/extensions/sf/workflow-migration.ts +++ b/src/resources/extensions/sf/workflow-migration.ts @@ -5,7 +5,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; -import { parsePlan, parseRoadmap } from "./parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "./parsers.js"; import { _getAdapter, bulkInsertLegacyHierarchy } from "./sf-db.js"; import { logWarning } from "./workflow-logger.js"; diff --git a/src/resources/extensions/sf/workflow-plugins.ts b/src/resources/extensions/sf/workflow-plugins.ts index 8e23d4903..f1dd1662d 100644 --- a/src/resources/extensions/sf/workflow-plugins.ts +++ b/src/resources/extensions/sf/workflow-plugins.ts @@ -19,8 +19,7 @@ import { existsSync, statSync, } from "node:fs"; -import { join, dirname, extname, basename } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join, extname, basename } from "node:path"; import { homedir } from "node:os"; import { parse as parseYaml } from "yaml"; @@ -63,7 +62,7 @@ export interface WorkflowPlugin { const sfHome = process.env.SF_HOME || join(homedir(), ".sf"); function resolveBundledDir(): string { - const moduleDir = dirname(fileURLToPath(import.meta.url)); + const moduleDir = import.meta.dirname; const local = join(moduleDir, "workflow-templates"); if (existsSync(local)) return local; const agentSfDir = join(sfHome, "agent", "extensions", "sf", "workflow-templates"); diff --git a/src/resources/extensions/sf/workflow-templates.ts b/src/resources/extensions/sf/workflow-templates.ts index 6b406587b..256be3988 100644 --- a/src/resources/extensions/sf/workflow-templates.ts +++ b/src/resources/extensions/sf/workflow-templates.ts @@ -7,8 +7,7 @@ import { existsSync, readFileSync } from "node:fs"; import { homedir } from "node:os"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; const __extensionDir = resolveSfExtensionDir(); const registryPath = join( @@ -22,7 +21,7 @@ const registryPath = join( * Resolve the SF extension directory with fallback to ~/.sf/agent/extensions/sf/. */ function resolveSfExtensionDir(): string { - const moduleDir = dirname(fileURLToPath(import.meta.url)); + const moduleDir = import.meta.dirname; if (existsSync(join(moduleDir, "workflow-templates"))) return moduleDir; const sfHome = process.env.SF_HOME || join(homedir(), ".sf"); const agentSfDir = join(sfHome, "agent", "extensions", "sf"); diff --git a/src/resources/extensions/sf/workspace-index.ts b/src/resources/extensions/sf/workspace-index.ts index dd163a4f5..2939831ff 100644 --- a/src/resources/extensions/sf/workspace-index.ts +++ b/src/resources/extensions/sf/workspace-index.ts @@ -1,6 +1,6 @@ import { loadFile } from "./files.js"; import { findMilestoneIds } from "./guided-flow.js"; -import { parsePlan, parseRoadmap } from "./parsers-legacy.js"; +import { parsePlan, parseRoadmap } from "./parsers.js"; import { resolveMilestoneFile, resolveSliceFile, diff --git a/src/resources/extensions/ttsr/index.ts b/src/resources/extensions/ttsr/index.ts index 704834193..d0754edbe 100644 --- a/src/resources/extensions/ttsr/index.ts +++ b/src/resources/extensions/ttsr/index.ts @@ -14,8 +14,7 @@ */ import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; import type { AssistantMessageEvent } from "@singularity-forge/pi-ai"; import type { ExtensionAPI } from "@singularity-forge/pi-coding-agent"; import { loadRules } from "./rule-loader.js"; @@ -25,8 +24,7 @@ import { type TtsrMatchContext, } from "./ttsr-manager.js"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const __dirname = import.meta.dirname; interface PendingViolation { rules: Rule[]; diff --git a/src/resources/extensions/universal-config/package.json b/src/resources/extensions/universal-config/package.json index 5c69bea18..dfa821dc2 100644 --- a/src/resources/extensions/universal-config/package.json +++ b/src/resources/extensions/universal-config/package.json @@ -3,6 +3,9 @@ "private": true, "version": "1.0.0", "type": "module", + "engines": { + "node": ">=24.0.0" + }, "pi": { "extensions": [ "./index.ts" diff --git a/src/tests/app-smoke.test.ts b/src/tests/app-smoke.test.ts index bcd1b2724..9ac53c321 100644 --- a/src/tests/app-smoke.test.ts +++ b/src/tests/app-smoke.test.ts @@ -11,7 +11,7 @@ */ import assert from "node:assert/strict"; -import { execSync } from "node:child_process"; +import { execFileSync, execSync } from "node:child_process"; import { existsSync, mkdirSync, @@ -246,17 +246,17 @@ test("loader exits with error on unsupported Node version", () => { const script = [ "const major = parseInt(process.versions.node.split('.')[0], 10);", "const MIN = 99;", - "if (major < MIN) { process.stderr.write('WOULD_EXIT'); process.exit(1); }", + "if (major < MIN) { require('node:fs').writeFileSync(2, 'WOULD_EXIT'); process.exit(1); }", "process.stdout.write('OK');", ].join(" "); try { - execSync(`node -e "${script}"`, { encoding: "utf-8", stdio: "pipe" }); + execFileSync("node", ["-e", script], { encoding: "utf-8", stdio: "pipe" }); // Node >= 99 would reach here — acceptable no-op } catch (err: unknown) { const e = err as { status?: number; stderr?: string }; assert.strictEqual(e.status, 1, "exits with code 1 for unsupported Node"); assert.ok( - (e.stderr || "").includes("WOULD_EXIT"), + String(e.stderr || "").includes("WOULD_EXIT"), "stderr contains version error", ); } @@ -276,7 +276,7 @@ test("loader MIN_NODE_MAJOR matches package.json engines field", () => { assert.ok(match, "MIN_NODE_MAJOR is defined with a numeric value"); const loaderMin = parseInt(match![1], 10); - // Extract major version from engines.node (e.g. ">=22.0.0" → 22) + // Extract major version from engines.node (e.g. ">=24.0.0" → 24) const engineMatch = (pkg.engines?.node || "").match(/(\d+)/); assert.ok(engineMatch, "package.json engines.node is defined"); const engineMin = parseInt(engineMatch![1], 10); diff --git a/src/tests/integration/web-boot-node24.test.ts b/src/tests/integration/web-boot-node24.test.ts index 2dcd051c7..534315b27 100644 --- a/src/tests/integration/web-boot-node24.test.ts +++ b/src/tests/integration/web-boot-node24.test.ts @@ -7,10 +7,6 @@ import { resolveTypeStrippingFlag } from "../../web/ts-subprocess-flags.ts"; // Bug 1 — resolveTypeStrippingFlag selects the correct flag // --------------------------------------------------------------------------- -const [nodeMajor, nodeMinor] = process.versions.node.split(".").map(Number); -const isNode22_7OrNewer = - nodeMajor > 22 || (nodeMajor === 22 && nodeMinor >= 7); - test("resolveTypeStrippingFlag returns --experimental-strip-types for paths outside node_modules", () => { const flag = resolveTypeStrippingFlag("/home/user/projects/sf"); assert.equal(flag, "--experimental-strip-types"); @@ -22,39 +18,18 @@ test("resolveTypeStrippingFlag returns --experimental-strip-types for path with assert.equal(flag, "--experimental-strip-types"); }); -test("resolveTypeStrippingFlag returns --experimental-transform-types for paths under node_modules/ on Node >= 22.7", { - skip: !isNode22_7OrNewer, -}, () => { +test("resolveTypeStrippingFlag returns --experimental-transform-types for paths under node_modules/", () => { const flag = resolveTypeStrippingFlag("/usr/lib/node_modules/sf-run"); assert.equal(flag, "--experimental-transform-types"); }); -test("resolveTypeStrippingFlag returns --experimental-strip-types for paths under node_modules/ on Node < 22.7", { - skip: isNode22_7OrNewer, -}, () => { - const flag = resolveTypeStrippingFlag("/usr/lib/node_modules/sf-run"); - // On older Node, falls back to strip-types since transform-types isn't available - assert.equal(flag, "--experimental-strip-types"); -}); - -test("resolveTypeStrippingFlag handles Windows-style paths under node_modules on Node >= 22.7", { - skip: !isNode22_7OrNewer, -}, () => { +test("resolveTypeStrippingFlag handles Windows-style paths under node_modules", () => { const flag = resolveTypeStrippingFlag( "C:\\Users\\dev\\AppData\\node_modules\\sf-run", ); assert.equal(flag, "--experimental-transform-types"); }); -test("resolveTypeStrippingFlag handles Windows-style paths under node_modules on Node < 22.7", { - skip: isNode22_7OrNewer, -}, () => { - const flag = resolveTypeStrippingFlag( - "C:\\Users\\dev\\AppData\\node_modules\\sf-run", - ); - assert.equal(flag, "--experimental-strip-types"); -}); - // --------------------------------------------------------------------------- // Bug 2 — waitForBootReady fails fast on consecutive 5xx // --------------------------------------------------------------------------- diff --git a/src/tests/startup-perf.test.ts b/src/tests/startup-perf.test.ts index 2016552a6..6283d1963 100644 --- a/src/tests/startup-perf.test.ts +++ b/src/tests/startup-perf.test.ts @@ -127,9 +127,9 @@ describe("batch directory discovery", () => { // ─── Node.js compile cache ────────────────────────────────────────────────── describe("Node.js compile cache env setup", () => { - it("NODE_COMPILE_CACHE is settable on Node 22+", () => { + it("NODE_COMPILE_CACHE is settable on Node 24+", () => { const nodeVersion = parseInt(process.versions.node, 10); - if (nodeVersion >= 22) { + if (nodeVersion >= 24) { // Verify the env var mechanism works (does not throw) const original = process.env.NODE_COMPILE_CACHE; try { diff --git a/src/web-mode.ts b/src/web-mode.ts index 5c65cca3e..9d513f758 100644 --- a/src/web-mode.ts +++ b/src/web-mode.ts @@ -8,8 +8,7 @@ import { randomBytes } from "node:crypto"; import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"; import { request as httpRequest } from "node:http"; import { createServer } from "node:net"; -import { dirname, join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join, resolve } from "node:path"; import { appRoot, webPidFilePath as defaultWebPidFilePath, @@ -17,7 +16,7 @@ import { const DEFAULT_HOST = "127.0.0.1"; const DEFAULT_PACKAGE_ROOT = resolve( - dirname(fileURLToPath(import.meta.url)), + import.meta.dirname, "..", ); diff --git a/src/web/ts-subprocess-flags.ts b/src/web/ts-subprocess-flags.ts index a5c230117..fcc344abb 100644 --- a/src/web/ts-subprocess-flags.ts +++ b/src/web/ts-subprocess-flags.ts @@ -10,14 +10,11 @@ import { join } from "node:path"; * `--experimental-strip-types` fails deterministically. * * `--experimental-transform-types` applies a full TypeScript transform that - * works regardless of whether the file is under `node_modules/`. On older - * Node versions (< 22.7) that lack both flags, this falls back to - * `--experimental-strip-types` (the caller's loader handles the rest). + * works regardless of whether the file is under `node_modules/`. SF requires + * Node 24+, so this flag is always available. */ export function resolveTypeStrippingFlag(packageRoot: string): string { - const needsTransform = - isUnderNodeModules(packageRoot) && supportsTransformTypes(); - return needsTransform + return isUnderNodeModules(packageRoot) ? "--experimental-transform-types" : "--experimental-strip-types"; } @@ -99,12 +96,3 @@ export function buildSubprocessPrefixArgs( "--input-type=module", ]; } - -/** - * Returns true when the running Node version supports - * `--experimental-transform-types` (available since Node v22.7.0). - */ -function supportsTransformTypes(): boolean { - const [major, minor] = process.versions.node.split(".").map(Number); - return major > 22 || (major === 22 && minor >= 7); -} diff --git a/studio/package.json b/studio/package.json index fc7e0097e..8651199eb 100644 --- a/studio/package.json +++ b/studio/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@tailwindcss/vite": "^4.2.1", - "@types/node": "^22.18.6", + "@types/node": "^24.12.2", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^5.1.0", @@ -27,5 +27,8 @@ "electron-vite": "^5.0.0", "tailwindcss": "^4.2.1", "typescript": "^5.9.3" + }, + "engines": { + "node": ">=24.0.0" } } diff --git a/tests/fixtures/run.ts b/tests/fixtures/run.ts index e1f67c11c..5f33bfa80 100644 --- a/tests/fixtures/run.ts +++ b/tests/fixtures/run.ts @@ -1,10 +1,9 @@ import { readdirSync } from "fs"; -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; +import { join } from "path"; import { loadFixture, FixtureReplayer } from "./provider.ts"; import type { FixtureTurn, FixtureRecording } from "./provider.ts"; -const __dirname = dirname(fileURLToPath(import.meta.url)); +const __dirname = import.meta.dirname; const recordingsDir = join(__dirname, "recordings"); const files = readdirSync(recordingsDir) diff --git a/tests/live/run.ts b/tests/live/run.ts index b4ffa7099..eb9ff4e57 100644 --- a/tests/live/run.ts +++ b/tests/live/run.ts @@ -1,9 +1,8 @@ import { readdirSync } from "fs"; import { execFileSync } from "child_process"; -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; +import { join } from "path"; -const __dirname = dirname(fileURLToPath(import.meta.url)); +const __dirname = import.meta.dirname; if ((process.env.SF_LIVE_TESTS || process.env.SF_LIVE_TESTS) !== "1") { console.log("Skipping live tests (set SF_LIVE_TESTS=1 to enable)"); diff --git a/tests/smoke/run.ts b/tests/smoke/run.ts index b115edbbf..6bb57b20a 100644 --- a/tests/smoke/run.ts +++ b/tests/smoke/run.ts @@ -1,9 +1,8 @@ import { readdirSync } from "fs"; import { execFileSync } from "child_process"; -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; +import { join } from "path"; -const __dirname = dirname(fileURLToPath(import.meta.url)); +const __dirname = import.meta.dirname; const testFiles = readdirSync(__dirname) .filter((f) => f.startsWith("test-") && f.endsWith(".ts")) diff --git a/vscode-extension/README.md b/vscode-extension/README.md index 2e1f2840f..3cbe92322 100644 --- a/vscode-extension/README.md +++ b/vscode-extension/README.md @@ -7,7 +7,7 @@ Control the [SF-2 coding agent](https://github.com/singularity-forge/sf-run) dir ## Requirements - **SF-2** installed globally: `npm install -g sf-pi` -- **Node.js** >= 22.0.0 +- **Node.js** >= 24.0.0 - **Git** installed and on PATH - **VS Code** >= 1.95.0 diff --git a/vscode-extension/package-lock.json b/vscode-extension/package-lock.json index c7a0636db..8b0a2efd1 100644 --- a/vscode-extension/package-lock.json +++ b/vscode-extension/package-lock.json @@ -1,11 +1,11 @@ { - "name": "gsd-2", + "name": "sf-2", "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "gsd-2", + "name": "sf-2", "version": "0.3.0", "license": "MIT", "devDependencies": { @@ -14,6 +14,7 @@ "typescript": "^5.7.0" }, "engines": { + "node": ">=24.0.0", "vscode": "^1.95.0" } }, diff --git a/vscode-extension/package.json b/vscode-extension/package.json index cb623a62e..30e8a6606 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -31,6 +31,7 @@ }, "extensionKind": ["workspace"], "engines": { + "node": ">=24.0.0", "vscode": "^1.95.0" }, "categories": [ diff --git a/web/package-lock.json b/web/package-lock.json index 99b57aa9e..138fd3660 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -72,7 +72,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.3.1", "@tailwindcss/postcss": "^4.2.0", - "@types/node": "^22", + "@types/node": "^24", "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "esbuild": "^0.27.4", @@ -82,6 +82,9 @@ "tailwindcss": "^4.2.0", "tw-animate-css": "1.3.3", "typescript": "5.7.3" + }, + "engines": { + "node": ">=24.0.0" } }, "node_modules/@alloc/quick-lru": { @@ -113,9 +116,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "dev": true, "license": "MIT", "engines": { @@ -153,16 +156,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { "version": "7.29.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", @@ -197,16 +190,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", @@ -280,23 +263,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "dev": true, "license": "MIT", "dependencies": { @@ -310,9 +293,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -469,13 +452,16 @@ } }, "node_modules/@codemirror/lang-jinja": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@codemirror/lang-jinja/-/lang-jinja-6.0.0.tgz", - "integrity": "sha512-47MFmRcR8UAxd8DReVgj7WJN1WSAMT7OJnewwugZM4XiHWkOjgJQqvEM1NpMj9ALMPyxmlziEI1opH9IaEvmaw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-jinja/-/lang-jinja-6.0.1.tgz", + "integrity": "sha512-P5kyHLObzjtbGj16h+hyvZTxJhSjBEeSx4wMjbnAf3b0uwTy2+F0zGjMZL4PQOm/mh2eGZ5xUDVZXgwP783Nsw==", "license": "MIT", "dependencies": { + "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-html": "^6.0.0", "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.2.0", "@lezer/lr": "^1.4.0" @@ -639,9 +625,9 @@ } }, "node_modules/@codemirror/lang-yaml": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz", - "integrity": "sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.3.tgz", + "integrity": "sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -654,9 +640,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.2.tgz", - "integrity": "sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -688,9 +674,9 @@ } }, "node_modules/@codemirror/search": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz", - "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.0.tgz", + "integrity": "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -720,9 +706,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.40.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.40.0.tgz", - "integrity": "sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg==", + "version": "6.41.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.41.1.tgz", + "integrity": "sha512-ToDnWKbBnke+ZLrP6vgTTDScGi5H37YYuZGniQaBzxMVdtCxMrslsmtnOvbPZk4RX9bvkQqnWR/WS/35tJA0qg==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.6.0", @@ -738,21 +724,21 @@ "license": "MIT" }, "node_modules/@emnapi/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", - "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", - "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "license": "MIT", "optional": true, "dependencies": { @@ -760,9 +746,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, @@ -771,9 +757,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", - "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", "cpu": [ "ppc64" ], @@ -788,9 +774,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", - "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", "cpu": [ "arm" ], @@ -805,9 +791,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", - "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", "cpu": [ "arm64" ], @@ -822,9 +808,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", - "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", "cpu": [ "x64" ], @@ -839,9 +825,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", - "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", "cpu": [ "arm64" ], @@ -856,9 +842,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", - "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", "cpu": [ "x64" ], @@ -873,9 +859,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", - "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", "cpu": [ "arm64" ], @@ -890,9 +876,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", - "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", "cpu": [ "x64" ], @@ -907,9 +893,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", - "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", "cpu": [ "arm" ], @@ -924,9 +910,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", - "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", "cpu": [ "arm64" ], @@ -941,9 +927,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", - "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", "cpu": [ "ia32" ], @@ -958,9 +944,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", - "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", "cpu": [ "loong64" ], @@ -975,9 +961,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", - "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", "cpu": [ "mips64el" ], @@ -992,9 +978,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", - "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", "cpu": [ "ppc64" ], @@ -1009,9 +995,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", - "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", "cpu": [ "riscv64" ], @@ -1026,9 +1012,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", - "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", "cpu": [ "s390x" ], @@ -1043,9 +1029,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", "cpu": [ "x64" ], @@ -1060,9 +1046,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", - "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", "cpu": [ "arm64" ], @@ -1077,9 +1063,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", - "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", "cpu": [ "x64" ], @@ -1094,9 +1080,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", - "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", "cpu": [ "arm64" ], @@ -1111,9 +1097,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", - "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", "cpu": [ "x64" ], @@ -1128,9 +1114,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", - "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", "cpu": [ "arm64" ], @@ -1145,9 +1131,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", - "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", "cpu": [ "x64" ], @@ -1162,9 +1148,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", - "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", "cpu": [ "arm64" ], @@ -1179,9 +1165,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", - "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", "cpu": [ "ia32" ], @@ -1196,9 +1182,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", - "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", "cpu": [ "x64" ], @@ -1404,29 +1390,43 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1548,6 +1548,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1564,6 +1567,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1580,6 +1586,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1596,6 +1605,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1612,6 +1624,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1628,6 +1643,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1644,6 +1662,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1660,6 +1681,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1676,6 +1700,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1698,6 +1725,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1720,6 +1750,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1742,6 +1775,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1764,6 +1800,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1786,6 +1825,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1808,6 +1850,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1830,6 +1875,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1972,9 +2020,9 @@ } }, "node_modules/@lezer/common": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.1.tgz", - "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", "license": "MIT" }, "node_modules/@lezer/cpp": { @@ -1989,9 +2037,9 @@ } }, "node_modules/@lezer/css": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.1.tgz", - "integrity": "sha512-PYAKeUVBo3HFThruRyp/iK91SwiZJnzXh8QzkQlwijB5y+N5iB28+iLk78o2zmKqqV0uolNhCwFqB8LA7b0Svg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -2064,9 +2112,9 @@ } }, "node_modules/@lezer/lr": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", - "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0" @@ -2235,6 +2283,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2251,6 +2302,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2267,6 +2321,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2283,6 +2340,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4178,49 +4238,49 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", - "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", + "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", - "lightningcss": "1.31.1", + "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.1" + "tailwindcss": "4.2.4" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", - "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", + "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-x64": "4.2.1", - "@tailwindcss/oxide-freebsd-x64": "4.2.1", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-x64-musl": "4.2.1", - "@tailwindcss/oxide-wasm32-wasi": "4.2.1", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" + "@tailwindcss/oxide-android-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-x64": "4.2.4", + "@tailwindcss/oxide-freebsd-x64": "4.2.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-x64-musl": "4.2.4", + "@tailwindcss/oxide-wasm32-wasi": "4.2.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", - "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", + "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", "cpu": [ "arm64" ], @@ -4235,9 +4295,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", - "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", + "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", "cpu": [ "arm64" ], @@ -4252,9 +4312,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", - "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", + "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", "cpu": [ "x64" ], @@ -4269,9 +4329,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", - "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", + "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", "cpu": [ "x64" ], @@ -4286,9 +4346,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", - "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", + "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", "cpu": [ "arm" ], @@ -4303,13 +4363,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", - "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", + "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4320,13 +4383,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", - "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", + "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4337,13 +4403,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", - "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", + "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4354,13 +4423,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", - "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", + "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4371,9 +4443,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", - "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", + "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -4400,74 +4472,10 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.8.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.8.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", - "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", + "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", "cpu": [ "arm64" ], @@ -4482,9 +4490,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", - "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", + "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", "cpu": [ "x64" ], @@ -4499,17 +4507,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz", - "integrity": "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.4.tgz", + "integrity": "sha512-wgAVj6nUWAolAu8YFvzT2cTBIElWHkjZwFYovF+xsqKsW2ADxM/X2opxj5NsF/qVccAOjRNe8X2IdPzMsWyHTg==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.2.1", - "@tailwindcss/oxide": "4.2.1", + "@tailwindcss/node": "4.2.4", + "@tailwindcss/oxide": "4.2.4", "postcss": "^8.5.6", - "tailwindcss": "4.2.1" + "tailwindcss": "4.2.4" } }, "node_modules/@tybys/wasm-util": { @@ -4649,13 +4657,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", - "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/react": { @@ -4684,20 +4692,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", - "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz", + "integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/type-utils": "8.57.0", - "@typescript-eslint/utils": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/type-utils": "8.59.1", + "@typescript-eslint/utils": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4707,9 +4715,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.0", + "@typescript-eslint/parser": "^8.59.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -4723,16 +4731,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", - "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz", + "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3" }, "engines": { @@ -4744,18 +4752,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", - "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz", + "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.0", - "@typescript-eslint/types": "^8.57.0", + "@typescript-eslint/tsconfig-utils": "^8.59.1", + "@typescript-eslint/types": "^8.59.1", "debug": "^4.4.3" }, "engines": { @@ -4766,18 +4774,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", - "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz", + "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0" + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4788,9 +4796,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", - "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz", + "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==", "dev": true, "license": "MIT", "engines": { @@ -4801,21 +4809,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", - "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz", + "integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/utils": "8.59.1", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4826,13 +4834,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", - "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz", + "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==", "dev": true, "license": "MIT", "engines": { @@ -4844,21 +4852,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", - "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz", + "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.0", - "@typescript-eslint/tsconfig-utils": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/project-service": "8.59.1", + "@typescript-eslint/tsconfig-utils": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4868,7 +4876,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { @@ -4895,13 +4903,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -4910,17 +4918,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", - "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz", + "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0" + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4931,17 +4952,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", - "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz", + "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/types": "8.59.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4966,9 +4987,9 @@ } }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.25.8", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.8.tgz", - "integrity": "sha512-9Rr+liiBmK4xzZHszL+twNRJApthqmITBwDP3emNTtTrkBFN4gHlqfp+nodKmoVt1+bUH1qQCtyqt+7dbDTHiw==", + "version": "4.25.9", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.9.tgz", + "integrity": "sha512-QFAqr+pu6lDmNpAlecODcF49TlsrZ0bj15zPzfhiqSDl+Um3EsDLFLppixC7kFLn+rdDM2LTvVjn5CPvefpRgw==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -4993,9 +5014,9 @@ } }, "node_modules/@uiw/codemirror-extensions-langs": { - "version": "4.25.8", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-langs/-/codemirror-extensions-langs-4.25.8.tgz", - "integrity": "sha512-Dqt1702Kv0xvwJvVFC+gH7LsPDRrwuPQ8zBd5pBCANZ+hNvp74B9KObEmMtH0a19OGf1/vJ9GNtYIwLUAIjl4A==", + "version": "4.25.9", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-langs/-/codemirror-extensions-langs-4.25.9.tgz", + "integrity": "sha512-POcA4K5AQwjcHAebWTCRld4bUCMTGFdV8AFal1Pj6ymeKRqWc58cduryfjmAl8yfdTU0Kb3fF2bO08BeDzX7FQ==", "license": "MIT", "dependencies": { "@codemirror/lang-cpp": "^6.0.0", @@ -5033,9 +5054,9 @@ } }, "node_modules/@uiw/codemirror-themes": { - "version": "4.25.8", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.8.tgz", - "integrity": "sha512-U6ZSO9A+nsN8zvNddtwhxxpi33J9okb4Li9HdhAItApKjYM22IgC8XSpGfs+ABGfsp1u6NhDSfBR9vAh3oTWXg==", + "version": "4.25.9", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.9.tgz", + "integrity": "sha512-DAHKb/L9ELwjY4nCf/MP/mIllHOn4GQe7RR4x8AMJuNeh9nGRRoo1uPxrxMmUL/bKqe6kDmDbIZ2AlhlqyIJuw==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -5052,16 +5073,16 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.25.8", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.8.tgz", - "integrity": "sha512-A0aLOuJZm2yJ+U9GlMFwxwFciztjd5LhcAG4SMqFxdD58wH+sCQXuY4UU5J2hqgS390qAlShtUgREvJPUonbuQ==", + "version": "4.25.9", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.9.tgz", + "integrity": "sha512-HftqCBUYShAOH0pGi1CHP8vfm5L8fQ3+0j0VI6lQD6QpK+UBu3J7nxfEN5O/BXMilMNf9ZyFJRvRcuMMOLHMng==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.25.8", + "@uiw/codemirror-extensions-basic-setup": "4.25.9", "codemirror": "^6.0.0" }, "funding": { @@ -5189,6 +5210,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5203,6 +5227,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5217,6 +5244,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5231,6 +5261,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5245,6 +5278,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5259,6 +5295,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5273,6 +5312,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5287,6 +5329,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5391,9 +5436,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -5630,9 +5675,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.27", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", - "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", "funding": [ { "type": "opencollective", @@ -5649,8 +5694,8 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001774", + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -5682,9 +5727,9 @@ } }, "node_modules/axe-core": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", - "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", + "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==", "dev": true, "license": "MPL-2.0", "engines": { @@ -5719,9 +5764,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.8", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", - "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "version": "2.10.25", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.25.tgz", + "integrity": "sha512-QO/VHsXCQdnzADMfmkeOPvHdIAkoB7i0/rGjINPJEetLx75hNttVWGQ/jycHUDP9zZ9rupbm60WRxcwViB0MiA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -5731,9 +5776,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -5755,9 +5800,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "funding": [ { "type": "opencollective", @@ -5774,11 +5819,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -5788,15 +5833,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -5848,9 +5893,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001778", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", - "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", "funding": [ { "type": "opencollective", @@ -6428,9 +6473,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.313", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", - "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "version": "1.5.349", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.349.tgz", + "integrity": "sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==", "license": "ISC" }, "node_modules/embla-carousel": { @@ -6469,23 +6514,23 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, "license": "MIT", "dependencies": { @@ -6572,16 +6617,16 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", - "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", + "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", @@ -6593,8 +6638,7 @@ "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0", - "safe-array-concat": "^1.1.3" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -6661,9 +6705,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", - "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6674,32 +6718,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.4", - "@esbuild/android-arm": "0.27.4", - "@esbuild/android-arm64": "0.27.4", - "@esbuild/android-x64": "0.27.4", - "@esbuild/darwin-arm64": "0.27.4", - "@esbuild/darwin-x64": "0.27.4", - "@esbuild/freebsd-arm64": "0.27.4", - "@esbuild/freebsd-x64": "0.27.4", - "@esbuild/linux-arm": "0.27.4", - "@esbuild/linux-arm64": "0.27.4", - "@esbuild/linux-ia32": "0.27.4", - "@esbuild/linux-loong64": "0.27.4", - "@esbuild/linux-mips64el": "0.27.4", - "@esbuild/linux-ppc64": "0.27.4", - "@esbuild/linux-riscv64": "0.27.4", - "@esbuild/linux-s390x": "0.27.4", - "@esbuild/linux-x64": "0.27.4", - "@esbuild/netbsd-arm64": "0.27.4", - "@esbuild/netbsd-x64": "0.27.4", - "@esbuild/openbsd-arm64": "0.27.4", - "@esbuild/openbsd-x64": "0.27.4", - "@esbuild/openharmony-arm64": "0.27.4", - "@esbuild/sunos-x64": "0.27.4", - "@esbuild/win32-arm64": "0.27.4", - "@esbuild/win32-ia32": "0.27.4", - "@esbuild/win32-x64": "0.27.4" + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" } }, "node_modules/escalade": { @@ -6825,15 +6869,15 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", "dev": true, "license": "MIT", "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { @@ -6953,16 +6997,6 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -7027,9 +7061,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -7043,41 +7077,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", - "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-scope": { @@ -7360,12 +7360,12 @@ } }, "node_modules/framer-motion": { - "version": "12.36.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.36.0.tgz", - "integrity": "sha512-4PqYHAT7gev0ke0wos+PyrcFxI0HScjm3asgU8nSYa8YzJFuwgIvdj3/s3ZaxLq0bUSboIn19A2WS/MHwLCvfw==", + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", "license": "MIT", "dependencies": { - "motion-dom": "^12.36.0", + "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "tslib": "^2.4.0" }, @@ -7514,9 +7514,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", "dev": true, "license": "MIT", "dependencies": { @@ -7671,9 +7671,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "dev": true, "license": "MIT", "dependencies": { @@ -7965,6 +7965,19 @@ "semver": "^7.7.1" } }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -8487,9 +8500,9 @@ } }, "node_modules/lightningcss": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", - "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -8503,23 +8516,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.31.1", - "lightningcss-darwin-arm64": "1.31.1", - "lightningcss-darwin-x64": "1.31.1", - "lightningcss-freebsd-x64": "1.31.1", - "lightningcss-linux-arm-gnueabihf": "1.31.1", - "lightningcss-linux-arm64-gnu": "1.31.1", - "lightningcss-linux-arm64-musl": "1.31.1", - "lightningcss-linux-x64-gnu": "1.31.1", - "lightningcss-linux-x64-musl": "1.31.1", - "lightningcss-win32-arm64-msvc": "1.31.1", - "lightningcss-win32-x64-msvc": "1.31.1" + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", - "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" ], @@ -8538,9 +8551,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", - "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], @@ -8559,9 +8572,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", - "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], @@ -8580,9 +8593,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", - "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], @@ -8601,9 +8614,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", - "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], @@ -8622,13 +8635,16 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", - "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8643,13 +8659,16 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", - "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8664,13 +8683,16 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", - "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8685,13 +8707,16 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", - "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8706,9 +8731,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", - "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], @@ -8727,9 +8752,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", - "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], @@ -9740,12 +9765,12 @@ } }, "node_modules/motion": { - "version": "12.36.0", - "resolved": "https://registry.npmjs.org/motion/-/motion-12.36.0.tgz", - "integrity": "sha512-5BMQuktYUX8aEByKWYx5tR4X3G08H2OMgp46wTxZ4o7CDDstyy4A0fe9RLNMjZiwvntCWGDvs16sC87/emz4Yw==", + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz", + "integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==", "license": "MIT", "dependencies": { - "framer-motion": "^12.36.0", + "framer-motion": "^12.38.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -9766,9 +9791,9 @@ } }, "node_modules/motion-dom": { - "version": "12.36.0", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.36.0.tgz", - "integrity": "sha512-Ep1pq8P88rGJ75om8lTCA13zqd7ywPGwCqwuWwin6BKc0hMLkVfcS6qKlRqEo2+t0DwoUcgGJfXwaiFn4AOcQA==", + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", "license": "MIT", "dependencies": { "motion-utils": "^12.36.0" @@ -9787,9 +9812,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -9943,16 +9968,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-exports-info/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/node-pty": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", @@ -9964,9 +9979,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "license": "MIT" }, "node_modules/object-assign": { @@ -10092,18 +10107,18 @@ } }, "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", - "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", "license": "MIT", "dependencies": { - "oniguruma-parser": "^0.12.1", + "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } @@ -10271,9 +10286,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", + "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", "funding": [ { "type": "opencollective", @@ -10325,12 +10340,6 @@ "react-is": "^16.13.1" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -10415,9 +10424,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.71.2", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.2.tgz", - "integrity": "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA==", + "version": "7.75.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.75.0.tgz", + "integrity": "sha512-Ovv94H+0p3sJ7B9B5QxPuCP1u8V/cHuVGyH55cSwodYDtoJwK+fqk3vjfIgSX59I2U/bU4z0nRJ9HMLpNiWEmw==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -10431,9 +10440,9 @@ } }, "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, "node_modules/react-markdown": { @@ -10605,6 +10614,12 @@ "decimal.js-light": "^2.4.1" } }, + "node_modules/recharts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -10740,13 +10755,16 @@ } }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", "dev": true, "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -10816,15 +10834,15 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, @@ -10877,16 +10895,13 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "devOptional": true, + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/set-function-length": { @@ -10983,6 +10998,19 @@ "@img/sharp-win32-x64": "0.34.5" } }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "optional": true, + "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", @@ -11046,14 +11074,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -11391,16 +11419,16 @@ } }, "node_modules/tailwindcss": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", - "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", + "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { @@ -11418,14 +11446,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -11499,9 +11527,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -11659,16 +11687,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", - "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.1.tgz", + "integrity": "sha512-xqDcFVBmlrltH64lklOVp1wYxgJr6LVdg3NamBgH2OOQDLFdTKfIZXF5PfghrnXQKXZGTQs8tr1vL7fJvq8CTQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.0", - "@typescript-eslint/parser": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/utils": "8.57.0" + "@typescript-eslint/eslint-plugin": "8.59.1", + "@typescript-eslint/parser": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/utils": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11679,7 +11707,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/unbox-primitive": { @@ -11702,9 +11730,9 @@ } }, "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==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, diff --git a/web/package.json b/web/package.json index e24081820..aeaa38f08 100644 --- a/web/package.json +++ b/web/package.json @@ -3,6 +3,9 @@ "version": "0.1.0", "private": true, "type": "module", + "engines": { + "node": ">=24.0.0" + }, "scripts": { "dev": "next dev", "build": "next build --webpack", @@ -75,7 +78,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.3.1", "@tailwindcss/postcss": "^4.2.0", - "@types/node": "^22", + "@types/node": "^24", "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "esbuild": "^0.27.4",