diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index e17cac6..6315e75 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,9 +1,9 @@ { - "phase": 0, - "stage": "complete", + "phase": 1, + "stage": "verify", "milestone": "v0.7", "milestone_name": "test-harness-review", - "phase_role": "pre_execution", + "phase_role": "execution", "attempts": 0, - "updated_at": "2026-09-26T16:00:00Z" + "updated_at": "2026-09-26T16:40:00Z" } \ No newline at end of file diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md index 0536aef..58cb7b3 100644 --- a/.ciagent/PLAN.md +++ b/.ciagent/PLAN.md @@ -23,13 +23,15 @@ **Environment facts (unchanged):** python 3.11.2 (venv at apps/ai-service/.venv, pip-only — A-009), node v24.15.0, pnpm 12.3.4, SQLite shared `Settings().db_path` (`~/.nextcraft/data/nextcraft.db`), tests NEVER call the cloud (mock providers + httpx MockTransport), secrets env-only (D-014), runtime state under `~/.nextcraft/` (D-039). -**Baseline (probe-verified census, 2026-09-26):** 785 tests all green — pytest 671 (64 files), web node:test 53 (7 files), CLI node:test 61 (11 files). Zero coverage tooling. Zero framework timeouts (ad-hoc 20-30s polling deadlines + a 600s CLI bootstrap step). Zero markers despite a real three-tier split (fast-unit / real-uvicorn / real-userns). Web tests OUTSIDE the turbo pipeline. 1,716 warnings per pytest run. 81 inert `# noqa`, 5 orphaned eslint-disable comments, no ESLint at all (turbo lint is a no-op for 5 of 6 packages), 27 inert `# type: ignore`, no Python type checker, recharts@2.15.4 deprecated direct dep, version drift (hardcoded `FastAPI(version="0.3.0")` vs pyproject "0.2.0" vs shipped line). +**Baseline (probe-verified census, 2026-09-26, counts re-verified at P1 execution):** 785 tests all green — pytest 670 (64 files; census counted 671 — collection at P1 execution says 670, the correct number), web node:test 53 (7 files), CLI node:test 64 (11 files; census counted 61 — the v0.5.7 hotfix `cda6ab1` landed 3 deploy-stale-checkout tests on census day, after the count was taken). Zero coverage tooling. Zero framework timeouts (ad-hoc 20-30s polling deadlines + a 600s CLI bootstrap step). Zero markers despite a real three-tier split (fast-unit / real-uvicorn / real-userns). Web tests OUTSIDE the turbo pipeline. 1,716 warnings per pytest run. 81 inert `# noqa`, 5 orphaned eslint-disable comments, no ESLint at all (turbo lint is a no-op for 5 of 6 packages), 27 inert `# type: ignore`, no Python type checker, recharts@2.15.4 deprecated direct dep, version drift (hardcoded `FastAPI(version="0.3.0")` vs pyproject "0.2.0" vs shipped line). + +**Count discipline (binding, Task 1-2-05):** test counts shift when tests are cut (P2), env-gated (P1), or added; a count is a point-in-time fact. Every phase's ship notes and verification block MUST restate the CURRENT counts, measured at execution — never copy a stale number forward from an earlier phase. Where a count changes, the ship notes state the mechanism (e.g. "CLI 64 = 63 pass + 1 env-gated E2E skip"). **Verification hooks (every phase, release-blocking) — count discipline: each phase's ship MUST restate the current test counts (they shift after P2 cuts); the counts below are the P1 baseline.** -- `pnpm ai:test` — full pytest run (671 at baseline; fast subset via `pnpm ai:test:fast` = `-m "not real_server and not userns"`, seconds-scale) -- `pnpm web:test` — web `tsx --test` (53 at baseline) under c8; inside the turbo pipeline (`pnpm test` runs all three workspaces) -- `pnpm cli:test` — CLI node:test (61 at baseline, fresh-clone E2E env-gated out); `pnpm cli:test:e2e` = opt-in real 900s path +- `pnpm ai:test` — full pytest run (670 at baseline; fast subset via `pnpm ai:test:fast` = `-m "not real_server and not userns"` = 621 tests, 49 deselected; measured at P1 execution: ~56s fast / ~174s full) +- `pnpm web:test` — web `tsx --test` (53 at baseline); `pnpm web:test:cov` = the same run under c8 (coverage is additive, default `test` script unchanged); inside the turbo pipeline (`pnpm test` runs all three workspaces) +- `pnpm cli:test` — CLI node:test (64 at baseline: 63 pass + 1 visible env-gated E2E skip; census counted 61 pre-v0.5.7-hotfix — the deploy-stale-checkout tests `cda6ab1` landed after the census); `pnpm cli:test:cov` = the same run under c8; `pnpm cli:test:e2e` = opt-in real 900s path - Coverage reports — pytest-cov report (ai-service) + c8 reporters (web/cli), exposed as script targets - `pnpm ai:lint` — ruff check (RUF100 joins the select set in P4) - `pnpm typecheck` — 5 tsc projects; mypy joins via `ai:typecheck` in P5 @@ -47,9 +49,9 @@ ### Wave 1-1: coverage + timeout + markers (backend-engineer) - **Task 1-1-01** (backend-engineer): pytest-cov added to dev deps + `[tool.coverage]` config in `apps/ai-service/pyproject.toml` (source = `ai_service`, sensible branch/report settings); `scripts/test.sh` gains a coverage flag; `pnpm ai:test` produces a coverage report every run. c8 for web/cli around the existing `tsx --test` (D-052 — no runner swap, no vitest): c8 devDep in `apps/web` + `apps/cli`, wired into their test scripts (or a `test:cov` script), reporters configured per workspace. -- **Task 1-1-02** (backend-engineer): markers `fast` / `real_server` / `userns` registered in `[tool.pytest.ini_options]` under `--strict-markers` (D-053). **Unmarked tests default to fast** — zero churn on ~600 unit tests. Mark `real_server` on the real-uvicorn suites: `tests/api/test_e2e_credential_flow.py`, `tests/api/test_telemetry_ingest.py`, `tests/telemetry/test_durability.py`, `tests/sandbox/test_telemetry_wiring.py`, `tests/variants/test_environments.py` (+ any suite the census's definition catches at execution — a real uvicorn/process spin-up). Mark `userns` on the real-namespace suites: `tests/sandbox/test_isolation.py`, `tests/sandbox/test_resource_limits.py`, the sandbox-agent tests (`tests/sandbox/test_sandbox_agent.py`). Canonical fast run = `-m "not real_server and not userns"` aliased as `ai:test:fast` (root script + `scripts/test.sh` flag). **Grill G-105:** marker assignment is verified against each suite's actual mechanism at execution — real_server iff the test spins a real uvicorn/process, userns iff it enters a real unshare namespace. Census correction: `tests/sandbox/test_sandbox_agent.py` requires NO namespace (its docstring: plain stdlib process, in-process threads) — it gets NO tier marker; its 20s-deadline outlier (:542) gets `@pytest.mark.timeout` per D-054's outlier mechanism. P1 records a timing baseline (full-run + fast-subset wall times at the 671/53/61 counts) in the ship notes — measured numbers replace "seconds-scale" claims (MH-M2's <60s stays the ceiling). The hang probe proves BOTH tiers: a fast probe dies at 60s AND a marked probe dies at 180s — the collection-hook override is proven, not assumed. +- **Task 1-1-02** (backend-engineer): markers `fast` / `real_server` / `userns` registered in `[tool.pytest.ini_options]` under `--strict-markers` (D-053). **Unmarked tests default to fast** — zero churn on ~600 unit tests. Mark `real_server` on the real-uvicorn suites: `tests/api/test_e2e_credential_flow.py`, `tests/api/test_telemetry_ingest.py`, `tests/telemetry/test_durability.py`, `tests/sandbox/test_telemetry_wiring.py`, `tests/variants/test_environments.py` (+ any suite the census's definition catches at execution — a real uvicorn/process spin-up). Mark `userns` on the real-namespace suites: `tests/sandbox/test_isolation.py`, `tests/sandbox/test_resource_limits.py`, the sandbox-agent tests (`tests/sandbox/test_sandbox_agent.py`). Canonical fast run = `-m "not real_server and not userns"` aliased as `ai:test:fast` (root script + `scripts/test.sh` flag). **Grill G-105:** marker assignment is verified against each suite's actual mechanism at execution — real_server iff the test spins a real uvicorn/process, userns iff it enters a real unshare namespace. Census correction: `tests/sandbox/test_sandbox_agent.py` requires NO namespace (its docstring: plain stdlib process, in-process threads) — it gets NO tier marker; its 20s-deadline outlier (:542) gets `@pytest.mark.timeout` per D-054's outlier mechanism. P1 records a timing baseline (full-run + fast-subset wall times at the 670/53/64 counts) in the ship notes — measured numbers replace "seconds-scale" claims (MH-M2's <60s stays the ceiling). The hang probe proves BOTH tiers: a fast probe dies at 60s AND a marked probe dies at 180s — the collection-hook override is proven, not assumed. - **Task 1-1-03** (backend-engineer): pytest-timeout — `signal` method, **60s global** (comfortably above the 20-30s polling loops), **180s override for `real_server`/`userns`-marked tests via a conftest collection hook** (D-054); `@pytest.mark.timeout` available for known outliers. Linux-only target makes SIGALRM safe and accurate; the thread method can't cancel blocked calls. -- **Task 1-1-04** (backend-engineer): CLI integration-suite split — the install-script/daemon/doctor.integration CLI suites join the slow lane the same opt-in way as the E2E (mechanism per execution: env-gate or script file-selection), keeping default `cli:test` fast; the fresh-clone E2E is definitively env-gated (Task 1-2-02). +- **Task 1-1-04** (backend-engineer): CLI integration-suite split — the install-script/daemon/doctor.integration CLI suites join the slow lane the same opt-in way as the E2E (mechanism per execution: env-gate or script file-selection), keeping default `cli:test` fast; the fresh-clone E2E is definitively env-gated (Task 1-2-02). **EXECUTION DECISION (P1):** measured the three suites at 6.8s combined — default `cli:test` totals 7.0s; no split required (recorded per the task's own mechanism clause). The E2E alone is env-gated; the rest stay in the default lane. ### Wave 1-2: turbo/scripts wiring + leak fixes (frontend-engineer + backend-engineer) @@ -57,9 +59,9 @@ - **Task 1-2-02** (frontend-engineer): CLI fresh-clone E2E env-gated per D-055 — the 600s real bootstrap is KEPT (the real path is the value), excluded from default `cli:test` unless `CLI_E2E=1`, exposed as `cli:test:e2e` with a 900s per-test budget (600 + slack). - **Task 1-2-03** (backend-engineer): conftest leak fixes — `tests/conftest.py:80` mutates `os.environ["AI_PROVIDER"]` without restore: fix via monkeypatch (or explicit restore in teardown) so no test run leaks provider state into the process env; `seed_verified_identity`'s blanket `except Exception: pass` (conftest.py:74-75) narrowed to the expected idempotency exception type (or logged) — a swallowed seeding failure must never masquerade as a seeded store. - **Task 1-2-04** (frontend-engineer): path-invocation silent skip fixed — `apps/cli/tests/path-invocation.test.ts:14` returns early (passes silently when the binary is missing): report `t.skip` properly so the skip is visible in the run summary, never a silent green. -- **Task 1-2-05** (lead-developer): PLAN.md verification counts corrected and the count-discipline rule documented — every phase's verification block states the current counts (P1 baseline: 671 py / 53 web / 61 CLI; the census already caught this plan file drifting once — never again). +- **Task 1-2-05** (lead-developer): PLAN.md verification counts corrected and the count-discipline rule documented — every phase's verification block states the current counts (P1 baseline: 670 py / 53 web / 64 CLI; the census already caught this plan file drifting once — never again). -**Verification hooks P1:** all suites green at current counts (671 / 53 / 61); `pnpm ai:test:fast` completes seconds-scale; coverage reports produced in all three workspaces; a deliberately-hanging probe test proves the 60s timeout fires (then removed); `pnpm test` (turbo) runs web+cli+ai; `pnpm typecheck` (5 tsc) + `pnpm ai:lint` + `pnpm build` green. Warnings still present — they die in P3. +**Verification hooks P1:** all suites green at current counts (670 py / 53 web / 64 CLI = 63 pass + 1 visible E2E skip); `pnpm ai:test:fast` completes seconds-scale (measured ~56s); coverage reports produced in all three workspaces; a deliberately-hanging probe test proves the 60s timeout fires (then removed); `pnpm test` (turbo) runs web+cli+ai; `pnpm typecheck` (5 tsc) + `pnpm ai:lint` + `pnpm build` green. Warnings still present — they die in P3. --- @@ -166,7 +168,7 @@ - **MH-1c**: pytest-timeout `signal` method live — 60s global, 180s for marked suites via the conftest collection hook; proven by a deliberate hang probe (removed after the proof). - **MH-1d**: CLI fresh-clone E2E env-gated (D-055) — default `cli:test` excludes it; `CLI_E2E=1` + `cli:test:e2e` runs the real 600s bootstrap under a 900s budget. - **MH-1e**: Web tests inside the turbo pipeline — `web#test` task in turbo.json; root `test` + `web:test` scripts; `pnpm test` runs all three workspaces. -- **MH-1f**: Leak/skip hygiene — `AI_PROVIDER` env mutation restored (no post-run env leak, provable), `seed_verified_identity` swallow narrowed to the expected exception (or logged), path-invocation reports `t.skip` visibly (never a silent green), PLAN.md verification counts corrected (671/53/61 baseline) with the per-phase count-discipline rule in force. +- **MH-1f**: Leak/skip hygiene — `AI_PROVIDER` env mutation restored (no post-run env leak, provable), `seed_verified_identity` swallow narrowed to the expected exception (or logged), path-invocation reports `t.skip` visibly (never a silent green), PLAN.md verification counts corrected (670/53/64 baseline) with the per-phase count-discipline rule in force. ### Phase 2 diff --git a/.gitignore b/.gitignore index 666de88..0055915 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ yarn-error.log* # Test coverage coverage/ +.coverage .nyc_output/ # Python tooling caches diff --git a/apps/ai-service/pyproject.toml b/apps/ai-service/pyproject.toml index 53687cb..3b40130 100644 --- a/apps/ai-service/pyproject.toml +++ b/apps/ai-service/pyproject.toml @@ -30,6 +30,8 @@ dependencies = [ dev = [ "pytest>=9.1,<10", "pytest-asyncio>=1.4,<2", + "pytest-cov>=7.1,<8", + "pytest-timeout>=2.4,<3", "ruff>=0.14", ] @@ -39,6 +41,30 @@ include = ["ai_service*"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] +# P1 harness wiring (D-053/D-054): strict markers + a 60s global timeout. +# `timeout` also ships its own marker (`pytest.mark.timeout`); the 180s +# override for real_server/userns tests is applied by the collection hook +# in tests/conftest.py — marker-level values beat this global default. +markers = [ + "fast: in-process unit test (default tier — unmarked tests are fast)", + "real_server: spins a real uvicorn server process", + "userns: enters a real unshare user namespace", +] +addopts = "--strict-markers --timeout=60 --timeout-method=signal" + +[tool.coverage.run] +source = ["ai_service"] # tests/ excluded by construction — only the package is measured +branch = true + +[tool.coverage.report] +# exclude_also APPENDS to coverage.py's built-ins (which already cover +# `pragma: no cover` and `if TYPE_CHECKING:`); these add the __main__ +# entry-point guard (none in ai_service today — future-proof). +exclude_also = [ + "if __name__ == [\"']__main__[\"']:", + "if TYPE_CHECKING:", + "pragma: no cover", +] [tool.ruff] line-length = 100 diff --git a/apps/ai-service/scripts/test.sh b/apps/ai-service/scripts/test.sh index 1663ee4..a320b3a 100755 --- a/apps/ai-service/scripts/test.sh +++ b/apps/ai-service/scripts/test.sh @@ -1,5 +1,12 @@ #!/usr/bin/env bash # Test runner: pytest via venv — mock provider only, zero network calls. +# +# P1 harness wiring (Task 1-1-01/1-1-02): +# default full suite WITH coverage (term + missing lines) +# NO_COV=1 coverage off (bare pytest flags, no --cov args) +# FAST=1 fast subset only: -m "not real_server and not userns" +# +# Extra args pass through: `bash scripts/test.sh tests/api -k foo`. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" APP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" @@ -11,4 +18,21 @@ if [ ! -x "$VENV/bin/pytest" ]; then fi cd "$APP_DIR" -exec "$VENV/bin/pytest" -q "$@" \ No newline at end of file + +# Assemble args; pytest arg order: later options win, so user-passed "$@" +# always overrides the wiring below. +ARGS=("-q") +if [ "${FAST:-0}" = "1" ]; then + # Fast subset: exclude the real-server and user-namespace tiers. + # The fast loop is the dev loop — coverage evidence comes from the full run. + ARGS+=("-m" "not real_server and not userns") + NO_COV=1 +fi +if [ "${NO_COV:-0}" = "1" ]; then + # Opt-out: bare run, no coverage tooling at all. + ARGS+=() +else + ARGS+=("--cov=ai_service" "--cov-report=term-missing") +fi + +exec "$VENV/bin/pytest" "${ARGS[@]}" "$@" diff --git a/apps/ai-service/tests/api/test_e2e_credential_flow.py b/apps/ai-service/tests/api/test_e2e_credential_flow.py index d40cd8e..e8ed056 100644 --- a/apps/ai-service/tests/api/test_e2e_credential_flow.py +++ b/apps/ai-service/tests/api/test_e2e_credential_flow.py @@ -31,6 +31,11 @@ from ai_service.variants.store import SQLiteVariantStore from ai_service.voice.defense_store import SQLiteDefenseStore from tests.sandbox.test_isolation import USERSNS_AVAILABLE +# P1 tier marker (Task 1-1-02): this suite boots a REAL uvicorn server +# (uvicorn.Server + serve loop, below) — real_server tier, 180s timeout +# via the conftest collection hook. +pytestmark = [pytest.mark.real_server] + COACHING_JSON = json.dumps( { "summary": "Strong iteration.", diff --git a/apps/ai-service/tests/api/test_telemetry_ingest.py b/apps/ai-service/tests/api/test_telemetry_ingest.py index 8c2edbe..e921917 100644 --- a/apps/ai-service/tests/api/test_telemetry_ingest.py +++ b/apps/ai-service/tests/api/test_telemetry_ingest.py @@ -46,6 +46,12 @@ LEARNER = "learner-9" TASK = "task-9" _BASE_TS = datetime(2026, 9, 11, 12, 0, 0, tzinfo=UTC) +# P1 tier marker (Task 1-1-02): the queue-overflow leak-regression test +# boots a REAL uvicorn server (TestClient teardown would hide the leak) — +# real_server tier, 180s timeout via the conftest collection hook. +# The rest of the suite is in-process TestClient-only. +pytestmark = [pytest.mark.real_server] + KNOWN_KINDS = ("command", "file_diff", "run_result", "test_result", "activity") #: Shared test token — minted into every test URL, verified by the endpoint diff --git a/apps/ai-service/tests/conftest.py b/apps/ai-service/tests/conftest.py index 967c455..ab49451 100644 --- a/apps/ai-service/tests/conftest.py +++ b/apps/ai-service/tests/conftest.py @@ -1,6 +1,5 @@ """Test suite — conftest: mock provider only, zero network (enforced).""" -import os import shutil import tempfile from pathlib import Path @@ -18,6 +17,21 @@ from ai_service.main import create_app _HOME_ROOT = Path.home() / ".nextcraft" / "test-sandboxes" +# P1 harness wiring (D-054, Task 1-1-02): real-server and user-namespace +# tests legitimately poll 20-30s under full-suite load (uvicorn boot waits, +# namespace spawn/reap, telemetry round-trips). The 60s global timeout +# would clobber them; this hook widens ONLY the marked items to 180s. +# Marker-level timeouts beat the global default, so the override is +# structural, not config-ordering luck. +_SLOW_TIER_TIMEOUT_S = 180 + + +def pytest_collection_modifyitems(config, items) -> None: + for item in items: + if item.get_closest_marker("real_server") or item.get_closest_marker("userns"): + item.add_marker(pytest.mark.timeout(_SLOW_TIER_TIMEOUT_S)) + + @pytest.fixture() def sandbox_dir(tmp_path: Path) -> Path: """Per-test sandbox workdir root on a bind-mount-safe filesystem.""" @@ -52,16 +66,27 @@ def seed_verified_identity(store, learner_ids=SUITE_LEARNERS) -> None: A-304: records carry mock=True — the seed never masquerades as production verification. + + Idempotency is check-first (not exception-swallowed): a learner whose + seed record already exists (shared store) is skipped via get(); the + duplicate-id IntegrityError is the ONLY tolerated failure — any other + exception propagates, so a broken seeding can never masquerade as a + seeded store (P1 Task 1-2-03, MH-1f). """ from datetime import UTC, datetime + from sqlalchemy.exc import IntegrityError + from ai_service.identity.store import IdentityRecord for lid in learner_ids: + seed_id = f"seed-{lid}" + if store.get(seed_id) is not None: + continue # already seeded (shared store) try: store.insert( IdentityRecord( - id=f"seed-{lid}", + id=seed_id, learner_id=lid, status="verified", provider="mock", @@ -71,13 +96,17 @@ def seed_verified_identity(store, learner_ids=SUITE_LEARNERS) -> None: verified_at=datetime.now(UTC), ) ) - except Exception: - pass # already seeded (shared store) + except IntegrityError: + # Concurrent seeding won the insert race for this id — the + # record exists, which is all this fixture guarantees. + continue @pytest.fixture() -def settings(tmp_path: Path) -> Settings: - os.environ["AI_PROVIDER"] = "mock" +def settings(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Settings: + # P1 leak fix (Task 1-2-03, MH-1f): monkeypatch restores AI_PROVIDER on + # teardown — the run never leaks provider state into the process env. + monkeypatch.setenv("AI_PROVIDER", "mock") # Hermetic stores (v0.3.6): db_path/sandbox_dir default to ~/.nextcraft — # tests must never read or write real state; pin to the pytest tmp dir. return Settings( diff --git a/apps/ai-service/tests/grading/test_engine.py b/apps/ai-service/tests/grading/test_engine.py index d59a61b..fb7b737 100644 --- a/apps/ai-service/tests/grading/test_engine.py +++ b/apps/ai-service/tests/grading/test_engine.py @@ -425,7 +425,7 @@ class TestBoundary: import ai_service.grading.engine as engine_mod - code = open(engine_mod.__file__).read() + code = Path(engine_mod.__file__).read_text() assert "fastapi" not in code assert "from ..api" not in code and "from ..api." not in code # the one sanctioned agents import is the module-direct structured defense diff --git a/apps/ai-service/tests/sandbox/test_isolation.py b/apps/ai-service/tests/sandbox/test_isolation.py index 3e9f826..55f1c45 100644 --- a/apps/ai-service/tests/sandbox/test_isolation.py +++ b/apps/ai-service/tests/sandbox/test_isolation.py @@ -49,6 +49,11 @@ requires_userns = pytest.mark.skipif( reason="unshare/unprivileged user namespaces unavailable on this host", ) +# P1 tier marker (Task 1-1-02): every test here enters a REAL unshare +# user namespace via backend.spawn — userns tier, 180s timeout via the +# conftest collection hook. +pytestmark = [pytest.mark.userns] + # --- fixtures ------------------------------------------------------------------ diff --git a/apps/ai-service/tests/sandbox/test_manager.py b/apps/ai-service/tests/sandbox/test_manager.py index 0e74405..d06d2e3 100644 --- a/apps/ai-service/tests/sandbox/test_manager.py +++ b/apps/ai-service/tests/sandbox/test_manager.py @@ -292,6 +292,8 @@ async def test_start_reaps_markerless_and_malformed_workdirs( @requires_userns +@pytest.mark.userns # P1 (G-105): REAL unshare namespace — the only non-stub +# test in this suite; the rest are in-process StubBackend unit tests. async def test_real_unshare_backend_create_exec_destroy(tmp_path: Path) -> None: """End-to-end on a capable host: create → exec echo → destroy.""" # NOT under /tmp: the in-namespace tmpfs shadows host /tmp, hiding a diff --git a/apps/ai-service/tests/sandbox/test_resource_limits.py b/apps/ai-service/tests/sandbox/test_resource_limits.py index 26cdd9a..a86a52b 100644 --- a/apps/ai-service/tests/sandbox/test_resource_limits.py +++ b/apps/ai-service/tests/sandbox/test_resource_limits.py @@ -64,6 +64,11 @@ requires_userns = pytest.mark.skipif( reason="unshare/unprivileged user namespaces unavailable on this host", ) +# P1 tier marker (Task 1-1-02): every test here exercises REAL unshare +# user-namespace sandboxes under rlimits — userns tier, 180s timeout via +# the conftest collection hook. +pytestmark = [pytest.mark.userns] + # Tight limits for these probes — much smaller than production defaults so the # enforcement is observable in bounded time. diff --git a/apps/ai-service/tests/sandbox/test_sandbox_agent.py b/apps/ai-service/tests/sandbox/test_sandbox_agent.py index cec87ff..b78cbb3 100644 --- a/apps/ai-service/tests/sandbox/test_sandbox_agent.py +++ b/apps/ai-service/tests/sandbox/test_sandbox_agent.py @@ -509,6 +509,7 @@ class TestSpoolOnDisconnect: class TestReconnectFlush: + @pytest.mark.timeout(120) # P1 (D-054 outlier): 20s poll deadline + load headroom def test_reconnect_flushes_spool_in_order_no_loss( self, tmp_path: Path, fake_server: FakeWSServer, held_link: KillableProxy ) -> None: diff --git a/apps/ai-service/tests/sandbox/test_telemetry_wiring.py b/apps/ai-service/tests/sandbox/test_telemetry_wiring.py index 9c0c88f..13eb354 100644 --- a/apps/ai-service/tests/sandbox/test_telemetry_wiring.py +++ b/apps/ai-service/tests/sandbox/test_telemetry_wiring.py @@ -30,6 +30,11 @@ from ai_service.telemetry.store import SQLiteTraceStore from .test_isolation import USERSNS_AVAILABLE +# P1 tier marker (Task 1-1-02): the e2e wiring test boots a REAL uvicorn +# server the in-namespace capture agent dials over loopback — real_server +# tier, 180s timeout via the conftest collection hook. +pytestmark = [pytest.mark.real_server] + def _userns_probe() -> None: if not USERSNS_AVAILABLE: diff --git a/apps/ai-service/tests/telemetry/test_durability.py b/apps/ai-service/tests/telemetry/test_durability.py index fe1780b..3e56b37 100644 --- a/apps/ai-service/tests/telemetry/test_durability.py +++ b/apps/ai-service/tests/telemetry/test_durability.py @@ -30,6 +30,11 @@ from ai_service.telemetry.tokens import IngestToken from ..sandbox.test_isolation import USERSNS_AVAILABLE +# P1 tier marker (Task 1-1-02): both tests boot a REAL uvicorn ingest + +# REAL namespace sandboxes behind a killable TCP proxy — real_server +# tier, 180s timeout via the conftest collection hook. +pytestmark = [pytest.mark.real_server] + def _userns_probe() -> None: if not USERSNS_AVAILABLE: diff --git a/apps/ai-service/tests/variants/test_environments.py b/apps/ai-service/tests/variants/test_environments.py index 9ef0027..854fdfa 100644 --- a/apps/ai-service/tests/variants/test_environments.py +++ b/apps/ai-service/tests/variants/test_environments.py @@ -30,6 +30,12 @@ from ai_service.variants.templates import TEMPLATES, validate_simple_argv from ..conftest import SUITE_LEARNERS, seed_verified_identity +# P1 tier marker (Task 1-1-02): test_design_kind_e2e_real_server (below) +# boots a REAL uvicorn server + REAL namespace sandbox — real_server +# tier, 180s timeout via the conftest collection hook. The remaining +# classes here are in-process TestClient-only. +pytestmark = [pytest.mark.real_server] + DESIGN_LEARNER = "pilot-learner" # verified 18+ via the suite seed diff --git a/apps/cli/package.json b/apps/cli/package.json index 6234ef7..d8eb747 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -9,6 +9,8 @@ "scripts": { "dev": "tsx src/index.ts", "test": "tsx --test tests/*.test.ts", + "test:cov": "c8 --include=src/** --reporter=text --reporter=text-summary tsx --test tests/*.test.ts", + "test:e2e": "CLI_E2E=1 tsx --test tests/fresh-clone-e2e.test.ts", "typecheck": "tsc --noEmit", "build": "tsc -p tsconfig.build.json", "build:binary": "node scripts/build-binary.mjs" @@ -17,6 +19,7 @@ "@types/node": "^24.0.0", "tsx": "^4.23.0", "typescript": "^5.7.2", - "esbuild": "0.28.2" + "esbuild": "0.28.2", + "c8": "^10.1.3" } } diff --git a/apps/cli/tests/fresh-clone-e2e.test.ts b/apps/cli/tests/fresh-clone-e2e.test.ts index 0575bee..1215ff6 100644 --- a/apps/cli/tests/fresh-clone-e2e.test.ts +++ b/apps/cli/tests/fresh-clone-e2e.test.ts @@ -9,64 +9,76 @@ import { fileURLToPath } from "node:url"; const cliDir = join(dirname(fileURLToPath(import.meta.url)), ".."); const repoRoot = join(cliDir, "..", ".."); -test("fresh-clone E2E: clone -> doctor -> bootstrap -> verify (happy path, transcript)", async () => { - const work = mkdtempSync(join(tmpdir(), "nc-e2e-")); - const cloneDir = join(work, "nextcraft"); +// P1 harness wiring (D-055, Task 1-2-02): the real 600s fresh-clone bootstrap +// is opt-in — it never runs inside the default `cli:test` suite. Setting +// CLI_E2E=1 (or `pnpm cli:test:e2e`) runs it; without the gate the test +// reports a VISIBLE skip with the reason, never a silent green. +test( + "fresh-clone E2E: clone -> doctor -> bootstrap -> verify (happy path, transcript)", + { + skip: process.env.CLI_E2E + ? false + : "env-gated: set CLI_E2E=1 (pnpm cli:test:e2e) to run the real 600s bootstrap", + }, + async () => { + const work = mkdtempSync(join(tmpdir(), "nc-e2e-")); + const cloneDir = join(work, "nextcraft"); - const clone = spawnSync( - "git", - ["clone", "--quiet", "--no-hardlinks", repoRoot, cloneDir], - { encoding: "utf8", timeout: 120000 }, - ); - assert.equal(clone.status, 0, `clone failed: ${clone.stderr}`); + const clone = spawnSync( + "git", + ["clone", "--quiet", "--no-hardlinks", repoRoot, cloneDir], + { encoding: "utf8", timeout: 120000 }, + ); + assert.equal(clone.status, 0, `clone failed: ${clone.stderr}`); - const binary = join(repoRoot, "apps", "cli", "dist", "nextcraft-linux-x64"); - const cli = existsSync(binary) - ? binary - : join(cliDir, "node_modules", ".bin", "tsx"); + const binary = join(repoRoot, "apps", "cli", "dist", "nextcraft-linux-x64"); + const cli = existsSync(binary) + ? binary + : join(cliDir, "node_modules", ".bin", "tsx"); - const transcript: string[] = []; - const step = (name: string, args: string[], expect: number, timeoutMs: number) => { - const { NODE_TEST_CONTEXT, ...restEnv } = process.env as Record; - const env = { - ...restEnv, - PATH: [join(process.env.HOME ?? "/home", ".local/bin"), process.env.PATH].filter(Boolean).join(":"), - NEXTCRAFT_VERSION: "v-e2e", - } as Record; - const run = spawnSync(cli, existsSync(binary) ? args : [join(cliDir, "src", "index.ts"), ...args], { - cwd: cloneDir, - encoding: "utf8", - timeout: timeoutMs, - env, - }); - transcript.push(`$ nextcraft ${args.join(" ")} -> exit ${run.status}`); - if (run.status !== expect) { - transcript.push(run.stdout, run.stderr); - assert.fail( - `${name} exited ${run.status} (expected ${expect})\nTRANSCRIPT:\n${transcript.join("\n")}\nstderr: ${run.stderr}`, - ); - } - return run.stdout + run.stderr; - }; + const transcript: string[] = []; + const step = (name: string, args: string[], expect: number, timeoutMs: number) => { + const { NODE_TEST_CONTEXT, ...restEnv } = process.env as Record; + const env = { + ...restEnv, + PATH: [join(process.env.HOME ?? "/home", ".local/bin"), process.env.PATH].filter(Boolean).join(":"), + NEXTCRAFT_VERSION: "v-e2e", + } as Record; + const run = spawnSync(cli, existsSync(binary) ? args : [join(cliDir, "src", "index.ts"), ...args], { + cwd: cloneDir, + encoding: "utf8", + timeout: timeoutMs, + env, + }); + transcript.push(`$ nextcraft ${args.join(" ")} -> exit ${run.status}`); + if (run.status !== expect) { + transcript.push(run.stdout, run.stderr); + assert.fail( + `${name} exited ${run.status} (expected ${expect})\nTRANSCRIPT:\n${transcript.join("\n")}\nstderr: ${run.stderr}`, + ); + } + return run.stdout + run.stderr; + }; - const doctorOut = step("doctor", ["doctor"], 0, 60000); - assert.ok(doctorOut.includes("node"), "doctor mentions node"); - assert.ok(doctorOut.includes("pnpm"), "doctor mentions pnpm"); + const doctorOut = step("doctor", ["doctor"], 0, 60000); + assert.ok(doctorOut.includes("node"), "doctor mentions node"); + assert.ok(doctorOut.includes("pnpm"), "doctor mentions pnpm"); - step("bootstrap", ["bootstrap"], 0, 600000); + step("bootstrap", ["bootstrap"], 0, 600000); - const verifyOut = step("verify", ["verify"], 0, 120000); - assert.ok(verifyOut.includes("venv"), "verify covers venv"); - assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".env")), ".env created in clone"); - assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".venv")), "venv created in clone"); - assert.ok( - existsSync(join(cloneDir, "node_modules", ".pnpm")), - "workspace node_modules present", - ); + const verifyOut = step("verify", ["verify"], 0, 120000); + assert.ok(verifyOut.includes("venv"), "verify covers venv"); + assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".env")), ".env created in clone"); + assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".venv")), "venv created in clone"); + assert.ok( + existsSync(join(cloneDir, "node_modules", ".pnpm")), + "workspace node_modules present", + ); - const envExample = readFileSync(join(repoRoot, "apps", "ai-service", ".env.example"), "utf8"); - const envClone = readFileSync(join(cloneDir, "apps", "ai-service", ".env"), "utf8"); - assert.equal(envClone, envExample, ".env content matches template"); + const envExample = readFileSync(join(repoRoot, "apps", "ai-service", ".env.example"), "utf8"); + const envClone = readFileSync(join(cloneDir, "apps", "ai-service", ".env"), "utf8"); + assert.equal(envClone, envExample, ".env content matches template"); - console.log("E2E TRANSCRIPT:\n" + transcript.join("\n")); -}); \ No newline at end of file + console.log("E2E TRANSCRIPT:\n" + transcript.join("\n")); + }, +); \ No newline at end of file diff --git a/apps/cli/tests/path-invocation.test.ts b/apps/cli/tests/path-invocation.test.ts index 5cdffb4..5b92186 100644 --- a/apps/cli/tests/path-invocation.test.ts +++ b/apps/cli/tests/path-invocation.test.ts @@ -10,8 +10,7 @@ const cliDir = join(dirname(fileURLToPath(import.meta.url)), ".."); const repoRoot = join(cliDir, "..", ".."); const binary = join(repoRoot, "apps", "cli", "dist", "nextcraft-linux-x64"); -test("PATH bare-word invocation: installed binary speaks (SEA regression, v0.3.4 bug)", () => { - if (!existsSync(binary)) return; // built by cli:build:binary; covered in ship flow +test("PATH bare-word invocation: installed binary speaks (SEA regression, v0.3.4 bug)", { skip: existsSync(binary) ? false : `built binary missing: ${binary} (run cli:build:binary)` }, () => { const binDir = mkdtempSync(join(tmpdir(), "nc-pathword-")); cpSync(binary, join(binDir, "nextcraft")); const env = { diff --git a/apps/web/package.json b/apps/web/package.json index 320ff5a..bddb9d7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,7 +10,8 @@ "typecheck": "tsc --noEmit", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", - "test": "tsx --test tests/*.test.ts" + "test": "tsx --test tests/*.test.ts", + "test:cov": "c8 --include=lib/** --include=hooks/** --include=components/marketplace/** --reporter=text --reporter=text-summary tsx --test tests/*.test.ts" }, "dependencies": { "@nextcraft/mock-data": "workspace:*", @@ -32,6 +33,7 @@ "@types/node": "20.17.6", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "c8": "^10.1.3", "storybook": "^10.6.0", "tailwindcss": "^4.0.0", "typescript": "^5.7.2", diff --git a/package.json b/package.json index abfb606..b2a84a5 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,14 @@ "ai:test": "turbo run test --filter=@nextcraft/ai-service", "ai:bootstrap": "turbo run bootstrap --filter=@nextcraft/ai-service", "ai:lint": "turbo run lint --filter=@nextcraft/ai-service", + "ai:test:fast": "cd apps/ai-service && FAST=1 bash scripts/test.sh", "cli:dev": "pnpm --filter @nextcraft/cli dev", + "test": "turbo run test", + "web:test": "pnpm --filter web test", + "web:test:cov": "pnpm --filter web test:cov", "cli:test": "pnpm --filter @nextcraft/cli test", + "cli:test:cov": "pnpm --filter @nextcraft/cli test:cov", + "cli:test:e2e": "pnpm --filter @nextcraft/cli test:e2e", "cli:typecheck": "pnpm --filter @nextcraft/cli typecheck", "cli:build": "pnpm --filter @nextcraft/cli build" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d068f5..524a240 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,6 +123,9 @@ importers: '@types/node': specifier: ^24.0.0 version: 24.13.4 + c8: + specifier: ^10.1.3 + version: 10.1.3 esbuild: specifier: 0.28.2 version: 0.28.2 @@ -187,6 +190,9 @@ importers: '@types/react-dom': specifier: ^19.0.0 version: 19.3.0(@types/react@19.3.0) + c8: + specifier: ^10.1.3 + version: 10.1.3 storybook: specifier: ^10.6.0 version: 10.6.0(@types/react@19.3.0)(react@19.3.0) @@ -821,6 +827,10 @@ packages: resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@emnapi/core@1.11.0': resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==} @@ -1160,6 +1170,14 @@ packages: cpu: [x64] os: [win32] + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1480,6 +1498,10 @@ packages: cpu: [x64] os: [win32] + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@pmmmwh/react-refresh-webpack-plugin@0.5.17': resolution: {integrity: sha512-tXDyE1/jzFsHXjhRZQ3hMl0IVhYe5qula43LDWIhVfjp9G/nT5OQY5AORVOrkEGAUltBJOfOWeETbmhm6kHhuQ==} engines: {node: '>= 10.13'} @@ -1913,6 +1935,9 @@ packages: '@types/html-minifier-terser@6.1.0': resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2093,6 +2118,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.3.0: + resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -2101,6 +2130,10 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2159,6 +2192,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -2182,6 +2219,13 @@ packages: brace-expansion@1.1.18: resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -2230,6 +2274,16 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} + c8@10.1.3: + resolution: {integrity: sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + monocart-coverage-reports: ^2 + peerDependenciesMeta: + monocart-coverage-reports: + optional: true + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -2297,6 +2351,10 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -2376,6 +2434,10 @@ packages: create-hmac@1.1.7: resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + crypto-browserify@3.12.1: resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} engines: {node: '>= 0.10'} @@ -2601,12 +2663,21 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.425: resolution: {integrity: sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojis-list@3.0.0: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} @@ -2720,6 +2791,10 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + find-up@6.3.0: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2735,6 +2810,10 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + fork-ts-checker-webpack-plugin@9.1.0: resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} engines: {node: '>=14.21.3'} @@ -2768,6 +2847,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2776,6 +2859,11 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -2827,6 +2915,9 @@ packages: html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-minifier-terser@6.1.0: resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} engines: {node: '>=12'} @@ -2902,6 +2993,10 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -2937,6 +3032,24 @@ packages: isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -3072,6 +3185,10 @@ packages: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + locate-path@7.2.0: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3095,6 +3212,9 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3117,6 +3237,10 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + map-or-similar@1.5.0: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} @@ -3167,9 +3291,17 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -3228,6 +3360,10 @@ packages: uglify-js: optional: true + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -3331,6 +3467,10 @@ packages: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3339,6 +3479,10 @@ packages: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + p-locate@6.0.0: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3347,6 +3491,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -3383,9 +3530,17 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3626,6 +3781,10 @@ packages: renderkid@3.0.0: resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -3735,6 +3894,14 @@ packages: '@types/node': optional: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -3751,6 +3918,10 @@ packages: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -3793,6 +3964,14 @@ packages: stream-parser@0.3.1: resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -3803,6 +3982,10 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -3907,6 +4090,10 @@ packages: engines: {node: '>=10'} hasBin: true + test-exclude@7.0.2: + resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} + engines: {node: '>=18'} + timers-browserify@2.0.12: resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} engines: {node: '>=0.6.0'} @@ -4040,6 +4227,10 @@ packages: deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} @@ -4083,6 +4274,19 @@ packages: resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -4106,9 +4310,25 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + yocto-queue@1.2.2: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} @@ -4892,6 +5112,8 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@1.0.2': {} + '@emnapi/core@1.11.0': dependencies: '@emnapi/wasi-threads': 1.2.2 @@ -5114,6 +5336,17 @@ snapshots: '@img/sharp-win32-x64@0.35.4': optional: true + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/schema@0.1.6': {} + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -5311,6 +5544,9 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.21.2': optional: true + '@pkgjs/parseargs@0.11.0': + optional: true + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@2.19.0)(webpack-hot-middleware@2.26.1)(webpack@5.110.3(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.28)(sharp@0.35.4(@types/node@20.17.6)))': dependencies: ansi-html: 0.0.9 @@ -5867,6 +6103,8 @@ snapshots: '@types/html-minifier-terser@6.1.0': {} + '@types/istanbul-lib-coverage@2.0.6': {} + '@types/json-schema@7.0.15': {} '@types/mdx@2.0.14': {} @@ -6098,12 +6336,16 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.3.0: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + argparse@2.0.1: {} aria-query@5.3.0: @@ -6177,6 +6419,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.11.21: {} @@ -6194,6 +6438,14 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.1.7: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -6267,6 +6519,20 @@ snapshots: dependencies: run-applescript: 7.1.0 + c8@10.1.3: + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@istanbuljs/schema': 0.1.6 + find-up: 5.0.0 + foreground-child: 3.3.1 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + test-exclude: 7.0.2 + v8-to-istanbul: 9.3.0 + yargs: 17.7.3 + yargs-parser: 21.1.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -6337,6 +6603,12 @@ snapshots: client-only@0.0.1: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} color-convert@2.0.1: @@ -6413,6 +6685,12 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.12 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + crypto-browserify@3.12.1: dependencies: browserify-cipher: 1.0.1 @@ -6644,6 +6922,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.425: {} elliptic@6.6.1: @@ -6656,6 +6936,10 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + emojis-list@3.0.0: {} endent@2.1.0: @@ -6775,6 +7059,11 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + find-up@6.3.0: dependencies: locate-path: 7.2.0 @@ -6792,6 +7081,11 @@ snapshots: dependencies: is-callable: 1.2.7 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.110.3(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.28)(sharp@0.35.4(@types/node@20.17.6))): dependencies: '@babel/code-frame': 7.29.7 @@ -6828,6 +7122,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -6846,6 +7142,15 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -6902,6 +7207,8 @@ snapshots: html-entities@2.6.0: {} + html-escaper@2.0.2: {} + html-minifier-terser@6.1.0: dependencies: camel-case: 4.1.2 @@ -6972,6 +7279,8 @@ snapshots: is-docker@3.0.0: {} + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -7010,6 +7319,27 @@ snapshots: isarray@2.0.5: {} + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jest-worker@27.5.1: dependencies: '@types/node': 20.17.6 @@ -7111,6 +7441,10 @@ snapshots: dependencies: p-locate: 4.1.0 + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + locate-path@7.2.0: dependencies: p-locate: 6.0.0 @@ -7131,6 +7465,8 @@ snapshots: dependencies: tslib: 2.8.1 + lru-cache@10.4.3: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -7153,6 +7489,10 @@ snapshots: dependencies: semver: 6.3.1 + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + map-or-similar@1.5.0: {} math-intrinsics@1.1.0: {} @@ -7197,10 +7537,18 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + minimatch@3.1.5: dependencies: brace-expansion: 1.1.18 + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.7 + minimist@1.2.8: {} minimizer-webpack-plugin@5.10.1(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.28)(sharp@0.35.4(@types/node@20.17.6))(webpack@5.110.3(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.28)(sharp@0.35.4(@types/node@20.17.6))): @@ -7218,6 +7566,8 @@ snapshots: postcss: 8.5.28 sharp: 0.35.4(@types/node@20.17.6) + minipass@7.1.3: {} + ms@2.0.0: {} ms@2.1.3: {} @@ -7386,6 +7736,10 @@ snapshots: dependencies: p-try: 2.2.0 + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + p-limit@4.0.0: dependencies: yocto-queue: 1.2.2 @@ -7394,12 +7748,18 @@ snapshots: dependencies: p-limit: 2.3.0 + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + p-locate@6.0.0: dependencies: p-limit: 4.0.0 p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + pako@1.0.11: {} param-case@3.0.4: @@ -7439,8 +7799,15 @@ snapshots: path-is-absolute@1.0.1: {} + path-key@3.1.1: {} + path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + path-type@4.0.0: {} pathval@2.0.1: {} @@ -7723,6 +8090,8 @@ snapshots: lodash: 4.18.1 strip-ansi: 6.0.1 + require-directory@2.1.1: {} + require-from-string@2.0.2: {} resolve-from@4.0.0: {} @@ -7843,6 +8212,12 @@ snapshots: '@types/node': 20.17.6 optional: true + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -7871,6 +8246,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + signal-exit@4.1.0: {} + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -7928,6 +8305,18 @@ snapshots: transitivePeerDependencies: - supports-color + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -7940,6 +8329,10 @@ snapshots: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.3.0 + strip-bom@3.0.0: {} strip-indent@3.0.0: @@ -7998,6 +8391,12 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + test-exclude@7.0.2: + dependencies: + '@istanbuljs/schema': 0.1.6 + glob: 10.5.0 + minimatch: 10.2.6 + timers-browserify@2.0.12: dependencies: setimmediate: 1.0.5 @@ -8121,6 +8520,12 @@ snapshots: uuid@9.0.1: {} + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + victory-vendor@36.9.2: dependencies: '@types/d3-array': 3.2.2 @@ -8213,6 +8618,22 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + wrappy@1.0.2: {} ws@8.21.3: {} @@ -8223,8 +8644,24 @@ snapshots: xtend@4.0.2: {} + y18n@5.0.8: {} + yallist@3.1.1: {} + yargs-parser@21.1.1: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + yocto-queue@1.2.2: {} zustand@4.5.7(@types/react@19.3.0)(react@19.3.0): diff --git a/turbo.json b/turbo.json index 69fd89a..d9696bf 100644 --- a/turbo.json +++ b/turbo.json @@ -35,6 +35,10 @@ "cache": false, "outputs": [] }, + "web#test": { + "cache": false, + "outputs": [] + }, "@nextcraft/cli#test": { "cache": false, "outputs": []