diff --git a/AGENTS.md b/AGENTS.md index 9104dd1f4..c9e229bf5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,16 @@ # AGENTS.md + +> **Agents: read the master context FIRST.** Before any work, read: +> +> - [`docs/architecture/naruon-product-spec.md`](docs/architecture/naruon-product-spec.md) for product vision, architecture, RBAC/ABAC, core features, AI agent ontology, branding/UX, observability, and governance; +> - the live [GitHub Project #1](https://github.com/orgs/ContextualWisdomLab/projects/1), the work and roadmap source of truth; +> - [`docs/product-technical-gap-baseline.md`](docs/product-technical-gap-baseline.md), the live gap snapshot but not merge authorization, and canonical completion issue [#1428](https://github.com/ContextualWisdomLab/naruon/issues/1428); +> - `ContextualWisdomLab/.github`'s [`docs/agent-github-project-protocol.md`](https://github.com/ContextualWisdomLab/.github/blob/main/docs/agent-github-project-protocol.md) for Project operation; and +> - `ContextualWisdomLab/.github`'s [`docs/product-goal-directive.md`](https://github.com/ContextualWisdomLab/.github/blob/main/docs/product-goal-directive.md) before running or configuring the continuous PR review→fix→merge→develop loop. A length-capped `/goal` pointer does not replace the full directive. +> +> This repository's Figma file ID is not yet canonical on `develop`: ADR-0013 and file ID `68b5XB58w8nwT2LYOOnikK` remain Proposed in open PR [#1436](https://github.com/ContextualWisdomLab/naruon/pull/1436). The repository and Project, not private agent memory, are authoritative. + ## Agent guidance (CWL governance) @@ -136,35 +147,23 @@ in this repo. `.github/workflows/opencode-review.yml`, `.github/workflows/strix.yml`, `.github/workflows/strix-selftest.yml`, or `.github/workflows/pr-review-merge-scheduler.yml`. -- The central Strix Security Scan uses GitHub Models by default through - `STRIX_GITHUB_MODELS_TOKEN`, `STRIX_LLM=openai/gpt-5`, and - `LLM_API_BASE_FILE` pointing at a trusted file containing - `https://models.github.ai/inference`; GitHub Models scans must try the - configured GPT-5-or-newer model first and may fall back to the explicit - workflow fallback list, currently - `github_models/deepseek/deepseek-r1-0528` and - `github_models/deepseek/deepseek-v3-0324`, when GitHub Models provider - capacity or model availability blocks the primary run. The Strix gate must - route these fallback names through the GitHub Models endpoint with - OpenAI-compatible child model names such as - `openai/deepseek/deepseek-r1-0528`, not the public DeepSeek API. Do not use - GPT-4.1 or weaker GitHub Models fallbacks for Strix or OpenCode PR review - evidence. Keep the GitHub Models endpoint in a trusted input file and pass - the token only through - the provider-scoped Strix child-process key path. Legacy `STRIX_LLM` secrets - must not override PR, push, or scheduled Strix defaults. Vertex remains - available only for manual - `workflow_dispatch` evidence when the `strix_llm` input - explicitly selects `vertex_ai/gemini-3.1-pro-preview-customtools` or - `vertex_ai/gemini-2.5-flash` with `GCP_SA_KEY`; expose Google/Vertex - credentials only for Vertex provider mode. Direct OpenAI GPT-5.4-or-newer - scans remain supported only for manual `strix_llm` selections with - `STRIX_OPENAI_API_KEY`. Do not silently fall back between providers, and - do not treat timeout-class provider infrastructure failures as clean PR - evidence even when Strix printed zero vulnerabilities before failing. Disable - silent Vertex fallback models in the workflow unless a future PR proves a new - exact fallback contract with no Timeout/Fatal/Warn/Denied output. Record - provider evidence in the PR. Known third-party Strix/Pydantic +- Central model-backed Actions are owned by `ContextualWisdomLab/.github` and + request only the logical `orchestrator/free` pool with a gateway token. Thin + callers in this repository must not select or forward provider names, model + names, provider groups, endpoints, or paid fallback credentials. +- `ContextualWisdomLab/contextual-orchestrator` owns provider discovery, + capability-based routing, free-pool membership, and fallback. Naruon owns + product/domain truth, authorization, tools, and context assembly. Production + use requires an immutable released owner API/client/schema; verify protected + release evidence before adoption and fail closed when that contract or a + required capability is unavailable. Do not copy owner source, query owner + storage, or treat an open PR or unreleased branch as a consumable contract. +- Do not impose a shared application/agent/gateway wall-clock timeout on model + work. A configured administrator limit, explicit user cancellation, or the + upstream provider's terminal result may end it; reasoning, streaming, and + tool calls are not failed merely because elapsed time is long. +- A failed model-backed check remains failed evidence even when an intermediate + report printed zero findings. Known third-party Strix/Pydantic serializer warnings must be filtered narrowly inside the Strix gate child process, not as a visible workflow env entry, so Warn-class logs are not accepted as clean evidence and warning-filter variable names do not pollute @@ -274,6 +273,19 @@ in this repo. ## Workspace and task tracking defaults +- Browser smoke must check collected console and page errors after interaction, + accessibility, screenshot capture, and page cleanup, not only after navigation. + Keep the early check for fast failure and a final check before returning success. + Regression tests must inject late errors into the actual route-smoke execution; + a successful screenshot or a passing render assertion does not prove clean interactions. + If screenshot retries are exhausted, throw the capture failure; do not create + diagnostic text files in the temporary capture directory or count them as screenshots. + Close the page in a finally block, including navigation and capture failures. +- Browser-smoke API mocks must reject unregistered requests with a non-success + response and record a harness failure even when the UI suppresses the error. + Cover requests emitted during cleanup; never return a default `{ ok: true }`. + Keep request URLs, query values, and payloads out of failure diagnostics. + - First-run frontend sessions should open the Today execution dashboard while preserving explicit Dashboard, Email, and Calendar startup choices. - Workspace navigation changes must keep the desktop primary nav and the @@ -457,10 +469,10 @@ in this repo. update both backend header tests and frontend link tests. Global backend responses must include `Referrer-Policy`, and `target="_blank"` links must use explicit `rel="noopener noreferrer"`. -- When robot review cites an obsolete Strix provider policy, update the docs and - tests to the current GitHub Models default contract before accepting a - rollback suggestion; do not reintroduce generic `LLM_API_KEY` or - cross-provider credential forwarding while trying to satisfy old comments. +- When robot review cites obsolete provider-specific Strix policy, compare it + with the released `.github` and `contextual-orchestrator` contracts before + accepting a rollback suggestion. Do not reintroduce direct-provider routing, + generic provider credentials, or cross-provider credential forwarding. - When reviews find inert navigation/dead-space controls, either wire them to an implemented workspace route/API or remove the control; do not leave high-traffic drawer/sidebar entries as permanent `준비 중` copy. @@ -568,6 +580,10 @@ in this repo. browser-submitted relationship classifications. Route layers should derive the thread provenance server-side, persist only scoped ontology metadata, and keep provider writes out of relationship capture. +- Relationship UI must derive customer wording from the bounded `next_action` + contract and must not render raw `action_reason`, provider rationale, source + keys, or machine action names. Unknown actions use a neutral next-step message; + browser coverage must assert both the customer text and absence of raw fields. - Unique email and forwarded-import dedupe must use strong scoped signals: normalized Message-ID, References/In-Reply-To, persisted duplicate provenance, or exact body/attachment fingerprints. Do not merge threads from subject-only @@ -649,12 +665,39 @@ in this repo. ## Development environment and tooling defaults +- Read the applicable repository skill before changing its contract: + [fix-development-mistakes](.agents/skills/fix-development-mistakes/SKILL.md) + for failures and security findings, + [github-actions-privileged-pr-scan](.agents/skills/github-actions-privileged-pr-scan/SKILL.md) + for privileged PR scanners, and + [github-robot-review-gate](.agents/skills/github-robot-review-gate/SKILL.md) + for check/review diagnosis. Record the failing reproduction, smallest causal + repair, exact verification command, and remaining gates in the existing PR. +- Visual Inspection requires opening the actual rendered pages or captured + images, not merely counting PNG files. Record app/build SHA separately from + runner SHA, viewport, routes, and observed defects. Mocked browser evidence + does not prove live provider behavior, all locales, or deployment readiness. + Inspect the changed AGENTS.md rendering on the pushed revision as well. +- A server-ready timeout before browser navigation is failed startup evidence, + not a completed visual inspection. Record the exact source and execution result; + host load alone does not prove the cause or justify increasing test timeouts. +- Preserve concurrent commits with ordinary history integration, then verify + the combined tree before pushing. Update these instructions with reusable + failure-prevention lessons; keep dated findings in the gap baseline/PR. +- Package registry credentials alone do not authorize or configure a cluster + deployment. Use the existing release workflow only after its protected-source, + checks, target credentials, and destination prerequisites are verified; report + package publication and live deployment as separate outcomes. +- Before relocating guidance out of a PR, verify its complete successor delta + and record the destination PR, commit, and path; a named owner alone is not succession. - If CodeGraph is not initialized for this repository, agents may run `codegraph init -i` autonomously without asking first; keep generated `.codegraph/` and `.cursor/rules/codegraph.mdc` artifacts local unless a future repository policy explicitly says to commit them. OpenCode PR review - uses the project `opencode.jsonc` MCP servers for CodeGraph, DeepWiki, - Context7, and web search. It must initialize CodeGraph before review so + receives the only `opencode.jsonc` from the central required workflow; do not + add repository-local `opencode.json` or `opencode.jsonc`. The central owner + registers review MCP servers, including Graphify. It must initialize + CodeGraph before review so structural findings cite graph-backed evidence instead of relying only on grep or raw file reads; use Context7 for current library docs, DeepWiki for repository documentation, and web search only for bounded external lookups. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9d2cbba18..0ad757b07 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -6,7 +6,7 @@ flowchart LR UI[Next.js frontend] --> API[FastAPI backend / Naruon control plane] API --> DB[(Postgres + pgvector)] - API --> LLM[OpenAI APIs when configured] + API --> LLM[contextual-orchestrator released consumer API] API --> CONN[Outbound-only self-hosted connector] CONN --> MAIL[Customer IMAP/POP3/SMTP] CONN --> DAV[Customer CalDAV/CardDAV/WebDAV] @@ -20,6 +20,15 @@ Runtime database connectivity is secret-injected: `backend/core/config.py` has no fallback `DATABASE_URL`, so missing database configuration fails at startup rather than silently using shared development credentials. +Production LLM work crosses an immutable released `contextual-orchestrator` +API/client/schema. `contextual-orchestrator` owns provider discovery, +capability-based routing, free-pool membership, and provider fallback; Naruon +owns product/domain truth, authorization, tools, and context assembly. A missing +or incompatible released owner contract fails closed instead of creating a +Naruon-local provider/model fallback. Local deterministic fixtures and legacy +provider surfaces may remain for migration/testing only when they are explicitly +identified as non-production authority. + ## Topic-intelligence boundary Naruon has no live Structural Topic Modeling endpoint, fitted topic artifact, @@ -83,7 +92,10 @@ registry/list/update/delete plus prompt-preview provider selection paths filter the authenticated organization. Existing local databases get the same fail-closed owner backfill through `scripts/bootstrap_db.py`; legacy provider rows require explicit non-default `NARUON_IMPORT_USER_ID` and `NARUON_IMPORT_ORGANIZATION_ID` -before bootstrap will set the new columns non-null. +before bootstrap will set the new columns non-null. This table and its direct +provider-routing fields are legacy migration surfaces, not the production +provider/model authority; new production LLM work must consume the released +`contextual-orchestrator` contract instead of extending this registry as a router. `ticket_tasks` stores email-derived execution items as ticket-like work records. The table and its new columns use at least two-word `snake_case` database names @@ -170,13 +182,15 @@ ETag/If-Match-guarded CalDAV/WebDAV PUT local adapter handlers for `SelfHostedConnector`; packaging, registration-token lifecycle, backend dispatch, and non-DAV protocol adapters remain separate connector delivery work. -LLM provider `base_url` is also a server-side egress boundary, not an arbitrary -URL field. Provider registry create/update paths are organization-admin scoped, -and LLM call sinks validate custom OpenAI-compatible base URLs with HTTPS-only -syntax, no userinfo/query or fragment, exact host membership in -`ALLOWED_LLM_BASE_URL_HOSTS`, and DNS answers that are all globally routable. -Missing allowlist configuration fails closed; the default provider path should -leave `base_url` unset. +The legacy LLM-provider `base_url` path remains a server-side egress boundary +while migration is incomplete, not an arbitrary URL field or production routing +authority. Existing provider registry create/update paths remain +organization-admin scoped, and current call sinks retain HTTPS-only syntax, +userinfo/query/fragment rejection, exact `ALLOWED_LLM_BASE_URL_HOSTS` membership, +and globally routable DNS validation until the legacy path is removed. Missing +allowlist configuration still fails closed. New production model work must not +select a provider endpoint through this surface; it uses the immutable released +`contextual-orchestrator` consumer contract. ## Batch embedding routing boundary @@ -192,12 +206,13 @@ SSRF-guarded, allowlisted, pinned-address HTTP client (`build_llm_provider_http_client`) that fronts every other outbound LLM call. naruon records a durable `llm_batch_jobs` / `llm_batch_items` audit trail (routing mode, the orchestrator's batch id, reported cost) for observability. -The path degrades gracefully: if batching is disabled, the orchestrator base URL -is rejected by the egress guard, or the orchestrator is unreachable, the caller -transparently falls back to the per-item embedding path. A local `pg-llm-batch` -package/checkout remains only as an optional offline-dev fallback, gated behind -orchestrator-unavailable and an explicit local DSN; naruon does not vendor a -gitlink for that fallback in this PR. +The currently implemented per-item fallback is migration behavior rather than +canonical provider authority: if batching is disabled or the orchestrator path +cannot be used, callers may still reach the legacy per-item embedding path, but +that path must not grow new production provider/model selection. A local +`pg-llm-batch` package/checkout remains only as an optional offline-dev fallback, +gated behind orchestrator-unavailable and an explicit local DSN; naruon does not +vendor a gitlink for that fallback in this PR. ## Semantic project-graph extractor seam @@ -216,16 +231,16 @@ and the runner degrades down the chain instead of losing the projection. New extractors (including future plugins on the platform plan's `kg.extractor` extension point) register a selector without editing ingest. -Routing LLM extraction through **contextual-orchestrator** is modelled as a -transport concern: the orchestrator is an OpenAI-compatible gateway, so the -`orchestrator` selector reuses the identical grounded LLM extractor -(`extract_project_semantics_llm`, which enforces segment citations) but points -its SSRF-allowlisted client (`build_llm_provider_http_client`) at -`PROJECT_GRAPH_ORCHESTRATOR_BASE_URL` instead of the raw provider. The provider -API key stays the tenant's Fernet-encrypted credential, and the orchestrator base -URL must be HTTPS and exact-host allowlisted by `ALLOWED_LLM_BASE_URL_HOSTS`; -an unset or rejected endpoint fails closed to the deterministic extractor. Design -and grounding: [`docs/architecture/kg-extractor-seam.md`](docs/architecture/kg-extractor-seam.md). +The current `orchestrator` selector still reuses the grounded LLM extractor and +an OpenAI-compatible transport adapter, but that compatibility layer is legacy +migration plumbing rather than Naruon-owned provider/model routing. It points the +SSRF-allowlisted client (`build_llm_provider_http_client`) at +`PROJECT_GRAPH_ORCHESTRATOR_BASE_URL`; its existing credential and allowlist +handling remains in place until the released owner client replaces it. New +production behavior must consume the immutable released `contextual-orchestrator` +API/client/schema and must not add raw-provider fallback here. An unset or +rejected endpoint fails closed to the deterministic extractor. Design and +grounding: [`docs/architecture/kg-extractor-seam.md`](docs/architecture/kg-extractor-seam.md). ## CI security boundary @@ -319,4 +334,4 @@ target source id, but it must not provide source ownership or capability records `backend/api/calendar.py` obtains writeback sources through a FastAPI dependency that reads DB-backed `calendar_writeback_sources` records scoped to the authenticated user. Provider execution remains opt-in and fail-closed without an -active outbound runner or If-Match/ETag evidence. +active outbound runner or If-Match/ETag evidence. \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index be67bc80c..1814c98ee 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,6 +17,9 @@ merged by metadata-only robot governance (see `docs/development/merge-gate-policy.md`); OpenCode Review, Strix Security Scan, and the merge scheduler come from central workflows in `ContextualWisdomLab/.github` — do not reintroduce repo-local copies. +That owner also supplies the only `opencode.jsonc` used by required review; +Naruon must not add `opencode.json` or `opencode.jsonc`. Central review MCP +registration, including Graphify, stays owner-controlled. ## Common commands @@ -110,11 +113,22 @@ indexes, and auditable writeback intent. `ARCHITECTURE.md` and ``` Next.js frontend ──> FastAPI backend (control plane) ──> Postgres + pgvector │ - ├──> OpenAI-compatible LLM providers (Ollama locally) + ├──> contextual-orchestrator consumer boundary + │ └──> provider/model routing owned by contextual-orchestrator └──> outbound-only self-hosted connector (connector/) └──> customer IMAP/POP3/SMTP + CalDAV/CardDAV/WebDAV ``` +Naruon owns product behavior and model-call authorization, but not provider/model routing. +Production model work requires an immutable released `contextual-orchestrator` +API/client/schema; a missing or incompatible owner contract fails closed instead +of falling back to a direct provider. The Ollama/MLX paths in local Compose are +explicit non-production development fixtures. Central model-backed GitHub Actions +are owned by `ContextualWisdomLab/.github` and use `orchestrator/free`; do not copy +that pool selection into Naruon runtime. Verify protected release evidence before +claiming the owner contract is available; an open PR or unreleased branch is only +proposed evidence. + - `backend/` — FastAPI app (`main.py`, routers in `api/`, domain logic in `services/`, SQLAlchemy models in `db/`, Alembic in `alembic/`). Owns persistence, canonical email threading @@ -140,8 +154,12 @@ Next.js frontend ──> FastAPI backend (control plane) ──> Postgres + pgve ETag/If-Match); intents are the default response. - Auth: signed HS256 bearer sessions (HMAC via `AUTH_SESSION_HMAC_SECRET`) or enterprise OIDC/JWKS; private `/api/*` routers register the default - `get_auth_context` dependency. LLM `base_url` and OIDC/SMTP/IMAP/POP3 hosts - are strict egress allowlists that resolve only to pinned global addresses. + `get_auth_context` dependency. Naruon owns domain tools, authorization, and + context assembly; provider/model routing belongs to `contextual-orchestrator`. + Existing direct-provider `base_url` surfaces are legacy migration debt and must + not be extended as production routing authority; keep their SSRF controls until + they are removed. OIDC/SMTP/IMAP/POP3 hosts remain strict egress allowlists + that resolve only to pinned global addresses. - CI (`.github/workflows/`): `app-ci.yml` (backend ruff+pytest, frontend test/lint/build), plus `bandit`, `codeql`, `trivy`, `scorecard`, `pr-governance`, `docker-publish` (GHCR on `v*` tags matching `VERSION`), and @@ -169,6 +187,9 @@ Next.js frontend ──> FastAPI backend (control plane) ──> Postgres + pgve HTTP status from message substrings. Error responses stay honest and do not leak internals or credential-type details (see "Error-message contract" in `README.md`). +- Relationship panels map bounded `next_action` values to customer-owned copy. + Never display raw `action_reason`, provider rationale, source keys, or machine + action names; use a neutral fallback and assert raw-field absence in browser tests. - Never commit `.env`, real mailbox exports, or credentials; email fixtures in `backend/tests/fixtures` stay small and synthetic. - Release bumps keep `VERSION`, `CHANGELOG.md`, `frontend/package.json`, and diff --git a/backend/tests/test_agent_llm_authority_docs.py b/backend/tests/test_agent_llm_authority_docs.py new file mode 100644 index 000000000..7104302a5 --- /dev/null +++ b/backend/tests/test_agent_llm_authority_docs.py @@ -0,0 +1,53 @@ +"""Regression contracts for current LLM-routing authority guidance.""" + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +def _read(path: str) -> str: + """Read repository guidance as UTF-8 text.""" + return (REPO_ROOT / path).read_text(encoding="utf-8") + + +def test_agent_guidance_does_not_reintroduce_direct_model_routing_authority() -> None: + """Current agent and architecture guidance must not prescribe direct routing.""" + agents = _read("AGENTS.md") + claude = _read("CLAUDE.md") + architecture = _read("ARCHITECTURE.md") + + forbidden_current_guidance = ( + "STRIX_GITHUB_MODELS_TOKEN", + "https://models.github.ai/inference", + "Direct OpenAI GPT-5.4-or-newer", + "OpenAI-compatible LLM providers", + "API --> LLM[OpenAI APIs when configured]", + ) + combined = f"{agents}\n{claude}\n{architecture}" + for phrase in forbidden_current_guidance: + assert phrase not in combined + + +def test_agent_guidance_names_canonical_llm_owner_and_fail_closed_boundary() -> None: + """Guidance must preserve product ownership while delegating LLM routing.""" + agents = _read("AGENTS.md") + claude = _read("CLAUDE.md") + architecture = _read("ARCHITECTURE.md") + + assert "ContextualWisdomLab/.github" in agents + assert "contextual-orchestrator" in agents + assert "orchestrator/free" in agents + assert "fail closed" in agents.lower() + assert "contextual-orchestrator" in claude + assert "Naruon owns" in claude + assert "provider/model routing" in claude + assert "gateway token" in agents + assert "provider names, model" in agents + assert "immutable released owner API/client/schema" in agents + assert "open PR or unreleased branch" in agents + assert "shared application/agent/gateway wall-clock timeout" in agents + assert "contextual-orchestrator" in architecture + assert "provider discovery" in architecture + assert "immutable released" in architecture + assert "fails closed" in architecture diff --git a/backend/tests/test_frontend_framework_security_floor.py b/backend/tests/test_frontend_framework_security_floor.py new file mode 100644 index 000000000..c1a7e2b9b --- /dev/null +++ b/backend/tests/test_frontend_framework_security_floor.py @@ -0,0 +1,316 @@ +"""Fail closed when frontend framework/image dependencies regress below patched floors.""" + +from __future__ import annotations + +import json +import re +from pathlib import Path +from typing import Any + +import pytest +import yaml + + +REPO_ROOT = Path(__file__).resolve().parents[2] +FRONTEND_ROOT = REPO_ROOT / "frontend" +NEXT_SECURITY_FLOOR = (16, 3, 3) +SHARP_SECURITY_FLOOR = (0, 35, 4) +JS_YAML_SECURITY_FLOOR = (4, 3, 2) +VITEST_SECURITY_FLOOR = (4, 1, 11) + + +def _exact_version(value: str) -> tuple[int, int, int]: + """Return a three-part exact version, rejecting ranges and prereleases.""" + + match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)", value) + assert match is not None, f"expected exact semantic version, got {value!r}" + return tuple(int(part) for part in match.groups()) + + +def _resolved_version(value: str) -> tuple[int, int, int]: + """Return the exact version prefix from a pnpm peer-qualified resolution.""" + + version = value.split("(", 1)[0] + return _exact_version(version) + + +def _package_key_version(package_key: str, package_name: str) -> tuple[int, int, int]: + """Return the version encoded by one pnpm package/snapshot key.""" + + prefix = f"{package_name}@" + assert package_key.startswith(prefix), ( + f"expected {package_name!r} lock key, got {package_key!r}" + ) + return _resolved_version(package_key[len(prefix) :]) + + +def _assert_lock_contract( + lock: dict[str, Any], + next_value: str, + eslint_next_value: str, + sharp_value: str, +) -> None: + """Validate root resolution identity and every locked Next.js/sharp security floor.""" + + importer = lock["importers"]["."] + next_import = importer["dependencies"]["next"] + assert next_import["specifier"] == next_value, ( + "root importer must preserve the package.json Next.js specifier" + ) + assert _resolved_version(str(next_import["version"])) == _exact_version(next_value), ( + "root importer must resolve the reviewed Next.js release" + ) + assert f"next@{next_import['version']}" in lock["snapshots"], ( + "root importer Next.js resolution must reference an existing snapshot" + ) + + eslint_next_import = importer["devDependencies"]["eslint-config-next"] + assert eslint_next_import["specifier"] == eslint_next_value, ( + "root importer must preserve the eslint-config-next specifier" + ) + assert _resolved_version(str(eslint_next_import["version"])) == _exact_version( + eslint_next_value + ), "root importer must resolve the reviewed eslint-config-next release" + assert f"eslint-config-next@{eslint_next_import['version']}" in lock["snapshots"], ( + "root importer eslint-config-next resolution must reference an existing snapshot" + ) + + assert str(lock["overrides"]["sharp"]) == sharp_value, ( + "lockfile sharp override must match the reviewed workspace override" + ) + + expected_next = _exact_version(next_value) + expected_sharp = _exact_version(sharp_value) + for section_name in ("packages", "snapshots"): + section = lock[section_name] + next_keys = [key for key in section if key.startswith("next@")] + sharp_keys = [key for key in section if key.startswith("sharp@")] + + assert next_keys, f"{section_name} must contain a Next.js resolution" + assert sharp_keys, f"{section_name} must contain a sharp resolution" + assert any( + _package_key_version(key, "next") == expected_next for key in next_keys + ), f"{section_name} must contain the reviewed Next.js release" + assert any( + _package_key_version(key, "sharp") == expected_sharp for key in sharp_keys + ), f"{section_name} must contain the reviewed sharp release" + + for package_key in next_keys: + assert _package_key_version(package_key, "next") >= NEXT_SECURITY_FLOOR, ( + f"{section_name} contains Next.js below the reviewed security floor: " + f"{package_key}" + ) + for package_key in sharp_keys: + assert _package_key_version(package_key, "sharp") >= SHARP_SECURITY_FLOOR, ( + f"{section_name} contains sharp below the reviewed security floor: " + f"{package_key}" + ) + + +def _frontend_security_inputs() -> tuple[str, str, str, dict[str, Any]]: + """Load the manifest, workspace override, and generated lock contract.""" + + package = json.loads((FRONTEND_ROOT / "package.json").read_text(encoding="utf-8")) + next_value = package["dependencies"]["next"] + eslint_next_value = package["devDependencies"]["eslint-config-next"] + workspace = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-workspace.yaml").read_text(encoding="utf-8") + ) + sharp_value = str(workspace["overrides"]["sharp"]) + lock = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8") + ) + return next_value, eslint_next_value, sharp_value, lock + + +def test_frontend_framework_and_image_security_floors() -> None: + """Keep manifests and every generated lock resolution at reviewed patched releases.""" + + next_value, eslint_next_value, sharp_value, lock = _frontend_security_inputs() + + assert _exact_version(next_value) >= NEXT_SECURITY_FLOOR, ( + "Next.js must include the fixes for CVE-2026-75604 and " + "GHSA-2xp9-vwfh-vxw4" + ) + assert eslint_next_value == next_value, ( + "eslint-config-next must stay on the same reviewed release as Next.js" + ) + assert _exact_version(sharp_value) >= SHARP_SECURITY_FLOOR, ( + "sharp must include the fix for GHSA-rgj7-g3m4-5g8c" + ) + _assert_lock_contract(lock, next_value, eslint_next_value, sharp_value) + + +def test_js_yaml_security_floor_covers_every_lock_resolution() -> None: + """Keep every js-yaml resolution above the reviewed denial-of-service floor.""" + + lock = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8") + ) + for section_name in ("packages", "snapshots"): + js_yaml_keys = [ + key for key in lock[section_name] if key.startswith("js-yaml@") + ] + for package_key in js_yaml_keys: + assert ( + _package_key_version(package_key, "js-yaml") + >= JS_YAML_SECURITY_FLOOR + ), f"{section_name} contains js-yaml below the reviewed security floor" + + +def test_vitest_security_floor_covers_manifest_and_lock() -> None: + """Keep Vitest and its coverage package above the reviewed traversal floor.""" + + package = json.loads((FRONTEND_ROOT / "package.json").read_text(encoding="utf-8")) + lock = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8") + ) + importer = lock["importers"]["."]["devDependencies"] + for package_name in ("vitest", "@vitest/coverage-v8"): + declared_value = package["devDependencies"][package_name] + assert _exact_version(declared_value) >= VITEST_SECURITY_FLOOR + importer_entry = importer[package_name] + assert importer_entry["specifier"] == declared_value, ( + f"root importer must preserve the package.json {package_name} specifier" + ) + assert _resolved_version(str(importer_entry["version"])) == _exact_version( + declared_value + ), f"root importer must resolve the reviewed {package_name} release" + assert f"{package_name}@{importer_entry['version']}" in lock["snapshots"], ( + f"root importer {package_name} resolution must reference an existing snapshot" + ) + for section_name in ("packages", "snapshots"): + package_keys = [ + package_key + for package_key in lock[section_name] + if package_key.startswith(f"{package_name}@") + ] + assert package_keys, ( + f"{section_name} must contain a {package_name} resolution" + ) + for package_key in package_keys: + assert ( + _package_key_version(package_key, package_name) + >= VITEST_SECURITY_FLOOR + ), f"{section_name} contains {package_name} below the reviewed floor" + + +@pytest.mark.parametrize("package_name", ["vitest", "@vitest/coverage-v8"]) +@pytest.mark.parametrize("section_name", ["packages", "snapshots"]) +def test_vitest_security_floor_rejects_missing_lock_resolution( + monkeypatch: pytest.MonkeyPatch, + package_name: str, + section_name: str, +) -> None: + """Reject a regenerated lock section that drops an expected Vitest resolution.""" + + package_text = (FRONTEND_ROOT / "package.json").read_text(encoding="utf-8") + lock = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8") + ) + lock[section_name] = { + key: value + for key, value in lock[section_name].items() + if not key.startswith(f"{package_name}@") + } + lock_text = yaml.safe_dump(lock) + original_read_text = Path.read_text + + def _read_text(path: Path, *args: Any, **kwargs: Any) -> str: + if path == FRONTEND_ROOT / "package.json": + return package_text + if path == FRONTEND_ROOT / "pnpm-lock.yaml": + return lock_text + return original_read_text(path, *args, **kwargs) + + monkeypatch.setattr(Path, "read_text", _read_text) + with pytest.raises(AssertionError): + test_vitest_security_floor_covers_manifest_and_lock() + + +@pytest.mark.parametrize("field", ["specifier", "version"]) +def test_security_floor_rejects_root_importer_drift(field: str) -> None: + """Reject a partially regenerated lock whose root Next.js importer drifts.""" + + next_value, eslint_next_value, sharp_value, lock = _frontend_security_inputs() + lock["importers"]["."]["dependencies"]["next"][field] = "16.3.2" + + with pytest.raises(AssertionError): + _assert_lock_contract(lock, next_value, eslint_next_value, sharp_value) + + +@pytest.mark.parametrize( + ("section_name", "package_key"), + [("packages", "next@16.3.2"), ("snapshots", "sharp@0.35.3")], +) +def test_security_floor_rejects_every_below_floor_lock_entry( + section_name: str, package_key: str +) -> None: + """Reject any stale vulnerable Next.js or sharp package/snapshot entry.""" + + next_value, eslint_next_value, sharp_value, lock = _frontend_security_inputs() + lock[section_name][package_key] = {} + + with pytest.raises(AssertionError): + _assert_lock_contract(lock, next_value, eslint_next_value, sharp_value) + + +@pytest.mark.parametrize("package_name", ["vitest", "@vitest/coverage-v8"]) +@pytest.mark.parametrize("field", ["specifier", "version"]) +def test_vitest_security_floor_rejects_root_importer_drift( + monkeypatch: pytest.MonkeyPatch, + package_name: str, + field: str, +) -> None: + """Reject a root Vitest importer that no longer matches the reviewed manifest.""" + + package_text = (FRONTEND_ROOT / "package.json").read_text(encoding="utf-8") + lock = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8") + ) + lock["importers"]["."]["devDependencies"][package_name][field] = "4.1.12" + lock_text = yaml.safe_dump(lock) + original_read_text = Path.read_text + + def _read_text(path: Path, *args: Any, **kwargs: Any) -> str: + if path == FRONTEND_ROOT / "package.json": + return package_text + if path == FRONTEND_ROOT / "pnpm-lock.yaml": + return lock_text + return original_read_text(path, *args, **kwargs) + + monkeypatch.setattr(Path, "read_text", _read_text) + with pytest.raises(AssertionError): + test_vitest_security_floor_covers_manifest_and_lock() + + +@pytest.mark.parametrize("package_name", ["vitest", "@vitest/coverage-v8"]) +def test_vitest_security_floor_rejects_missing_root_snapshot( + monkeypatch: pytest.MonkeyPatch, + package_name: str, +) -> None: + """Reject a root Vitest resolution whose exact peer-qualified snapshot vanished.""" + + package_text = (FRONTEND_ROOT / "package.json").read_text(encoding="utf-8") + lock = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8") + ) + resolution = str( + lock["importers"]["."]["devDependencies"][package_name]["version"] + ) + snapshot_key = f"{package_name}@{resolution}" + snapshot = lock["snapshots"].pop(snapshot_key) + lock["snapshots"][f"{package_name}@4.1.12"] = snapshot + lock_text = yaml.safe_dump(lock) + original_read_text = Path.read_text + + def _read_text(path: Path, *args: Any, **kwargs: Any) -> str: + if path == FRONTEND_ROOT / "package.json": + return package_text + if path == FRONTEND_ROOT / "pnpm-lock.yaml": + return lock_text + return original_read_text(path, *args, **kwargs) + + monkeypatch.setattr(Path, "read_text", _read_text) + with pytest.raises(AssertionError): + test_vitest_security_floor_covers_manifest_and_lock() diff --git a/backend/tests/test_js_yaml_dependency_security.py b/backend/tests/test_js_yaml_dependency_security.py new file mode 100644 index 000000000..11f1bc3ad --- /dev/null +++ b/backend/tests/test_js_yaml_dependency_security.py @@ -0,0 +1,50 @@ +"""Keep the generated frontend dependency graph on the reviewed js-yaml floor.""" + +from pathlib import Path + +import yaml + + +FRONTEND_ROOT = Path(__file__).resolve().parents[2] / "frontend" +JS_YAML_PATCHED_RELEASE = "4.3.2" + + +def _resolved_version(package_key: str) -> tuple[int, int, int]: + """Return the semantic version from one peer-qualified js-yaml lock key.""" + + prefix = "js-yaml@" + assert package_key.startswith(prefix) + version = package_key[len(prefix) :].split("(", 1)[0] + return tuple(int(part) for part in version.split(".")) + + +def test_js_yaml_override_lock_and_eslint_consumer_share_patched_release() -> None: + """Bind workspace policy, generated lock identity, and the ESLint consumer together.""" + + workspace = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-workspace.yaml").read_text(encoding="utf-8") + ) + lock = yaml.safe_load( + (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8") + ) + + assert str(workspace["overrides"]["js-yaml"]) == JS_YAML_PATCHED_RELEASE + assert str(lock["overrides"]["js-yaml"]) == JS_YAML_PATCHED_RELEASE + + floor = (4, 3, 2) + for section_name in ("packages", "snapshots"): + keys = [key for key in lock[section_name] if key.startswith("js-yaml@")] + assert keys, f"{section_name} must contain a js-yaml resolution" + assert {_resolved_version(key) for key in keys} == {floor} + + eslint_snapshots = [ + value + for key, value in lock["snapshots"].items() + if key.startswith("@eslint/eslintrc@") + ] + assert eslint_snapshots, "lock must retain the ESLint configuration snapshot" + assert any( + str(snapshot.get("dependencies", {}).get("js-yaml")) + == JS_YAML_PATCHED_RELEASE + for snapshot in eslint_snapshots + ), "ESLint must consume the reviewed js-yaml release" diff --git a/backend/tests/test_opencode_central_config_consumer.py b/backend/tests/test_opencode_central_config_consumer.py new file mode 100644 index 000000000..32b20a186 --- /dev/null +++ b/backend/tests/test_opencode_central_config_consumer.py @@ -0,0 +1,22 @@ +"""Contract for consuming OpenCode review configuration from the central owner.""" + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +def test_required_review_has_no_repository_local_opencode_configuration() -> None: + """Repository-local OpenCode JSON must not fork the central review policy.""" + + for relative_path in ("opencode.json", "opencode.jsonc"): + assert not (REPO_ROOT / relative_path).exists(), ( + "central OpenCode configuration must remain owner-controlled: " + f"{relative_path}" + ) + + agents = (REPO_ROOT / "AGENTS.md").read_text(encoding="utf-8") + claude = (REPO_ROOT / "CLAUDE.md").read_text(encoding="utf-8") + for guidance in (agents, claude): + assert "only `opencode.jsonc`" in guidance + assert "Graphify" in guidance diff --git a/backend/tests/test_release_governance.py b/backend/tests/test_release_governance.py index a23c70746..4d879f731 100644 --- a/backend/tests/test_release_governance.py +++ b/backend/tests/test_release_governance.py @@ -601,6 +601,20 @@ def test_scorecard_sarif_normalizer_rejects_escape_links_and_large_input( assert module.main([str(normalizer), str(expected)]) == 65 +def test_agents_entry_points_to_current_product_and_governance_sources() -> None: + agents = read_repo_text("AGENTS.md") + + assert "" in agents + assert "docs/architecture/naruon-product-spec.md" in agents + assert "docs/product-technical-gap-baseline.md" in agents + assert "ContextualWisdomLab/projects/1" in agents + assert "ContextualWisdomLab/naruon/issues/1428" in agents + assert "ContextualWisdomLab/naruon/pull/1436" in agents + assert "docs/agent-github-project-protocol.md" in agents + assert "docs/product-goal-directive.md" in agents + assert "not private agent memory" in agents + + def test_review_automation_uses_central_required_workflows_without_local_copies() -> ( None ): diff --git a/frontend/package.json b/frontend/package.json index 191b7c90b..f902cecd2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^1.27.0", - "next": "16.2.12", + "next": "16.3.4", "react": "19.2.8", "react-dom": "19.2.8", "react-resizable-panels": "^4.12.2", @@ -37,14 +37,14 @@ "@types/node": "^26", "@types/react": "^19", "@types/react-dom": "^19", - "@vitest/coverage-v8": "4.1.10", + "@vitest/coverage-v8": "4.1.11", "eslint": "^9", - "eslint-config-next": "16.2.12", + "eslint-config-next": "16.3.4", "fast-check": "^4.9.0", "jsdom": "^30.0.1", "postcss": "8.5.24", "typescript": "^6", - "vitest": "^4.1.10" + "vitest": "4.1.11" }, "overrides": { "brace-expansion": "5.0.9", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 610a0e7ca..58377eb5f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -6,8 +6,9 @@ settings: overrides: brace-expansion: 5.0.9 + js-yaml: 4.3.2 postcss: 8.5.24 - sharp: 0.35.0 + sharp: 0.35.4 undici: 8.9.0 pnpmfileChecksum: sha256-RXPq3MmEdRb3xD3rhbER9kciz9nBr/i0J/uMUjql5t0= @@ -38,8 +39,8 @@ importers: specifier: ^1.27.0 version: 1.27.0(react@19.2.8) next: - specifier: 16.2.12 - version: 16.2.12(@babel/core@7.29.7)(@playwright/test@1.62.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: 16.3.4 + version: 16.3.4(@babel/core@7.29.7)(@playwright/test@1.62.0)(@types/node@26.1.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: specifier: 19.2.8 version: 19.2.8 @@ -78,14 +79,14 @@ importers: specifier: ^19 version: 19.2.3(@types/react@19.2.17) '@vitest/coverage-v8': - specifier: 4.1.10 - version: 4.1.10(vitest@4.1.10) + specifier: 4.1.11 + version: 4.1.11(vitest@4.1.11) eslint: specifier: ^9 version: 9.39.5(jiti@2.7.0) eslint-config-next: - specifier: 16.2.12 - version: 16.2.12(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + specifier: 16.3.4 + version: 16.3.4(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) fast-check: specifier: ^4.9.0 version: 4.9.0 @@ -99,8 +100,8 @@ importers: specifier: ^6 version: 6.0.3 vitest: - specifier: ^4.1.10 - version: 4.1.10(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1)(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)) + specifier: 4.1.11 + version: 4.1.11(@types/node@26.1.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1)(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)) packages: @@ -289,6 +290,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -305,8 +312,8 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.6': - resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + '@eslint/eslintrc@3.3.7': + resolution: {integrity: sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.39.5': @@ -369,160 +376,160 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.35.0': - resolution: {integrity: sha512-ZgaYEwaj+lx/5n4W8GmZ2IYz0PQHjN5eqRcfijWGB+2Aq7ZInZGa0qJyAn6DEtyLuWHRSrmWOqT9q3qqTBvmUQ==} + '@img/sharp-darwin-arm64@0.35.4': + resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.35.0': - resolution: {integrity: sha512-c1z9LFpKB0slQW3RchwBE8iSVzGp70TNjUUO9k4BZwwW4HH7JBGHeIy4b+kk4n/kcBASb9evKCE3/7Slmslgiw==} + '@img/sharp-darwin-x64@0.35.4': + resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.0': - resolution: {integrity: sha512-Li2KTev0H90kEtnJHkI9xQojXt1AqWmFBMXiPw5kqd1jQgP7gi5HVK/qC5Rmh/59NuAwUuPzzPITmX22NomYYQ==} + '@img/sharp-freebsd-wasm32@0.35.4': + resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} engines: {node: '>=20.9.0'} os: [freebsd] - '@img/sharp-libvips-darwin-arm64@1.3.0': - resolution: {integrity: sha512-EKbmBKtyTH+GPFDRw2TgK2oV6hyxxlJVIar4hoTYSNmIwipgMFdxPQqR392GmfdsPGWga0mCFN1cCKjRb9cljw==} + '@img/sharp-libvips-darwin-arm64@1.3.3': + resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.0': - resolution: {integrity: sha512-Pl2OmOvrJ42adUllESxBsG54PfXLo1OYg9i3c5/5Ln/qJ0gZuTM9YMhQJPIbXqwidLRc/c2zuHt4RsrymmNv7A==} + '@img/sharp-libvips-darwin-x64@1.3.3': + resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.3.0': - resolution: {integrity: sha512-C0SqjoFKnszqa44EQ7xoaT48nnO0lOyXEULfXMWi8krrjOPGYkeK30Okzla6ATbBYsyZ0ySinK0FVkpv3DwzfQ==} + '@img/sharp-libvips-linux-arm64@1.3.3': + resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.3.0': - resolution: {integrity: sha512-A8UpHoUDW4DwnXoV6+q3C1s7QLRAHtPDEjWuNZjwHMyoCNZnm0GeNN8ls9f/bsEYTRQRW96C/n34XJQHJ2fT7A==} + '@img/sharp-libvips-linux-arm@1.3.3': + resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.3.0': - resolution: {integrity: sha512-WOpkVxAjFd369iaIzEgNRreFD+gWdUMIGD5zplhNKNeqS6mm5dac3q2AFyCBmzYoAdouzZvRBgxy4z8QHZb4/A==} + '@img/sharp-libvips-linux-ppc64@1.3.3': + resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.3.0': - resolution: {integrity: sha512-DRWw0mOHusrCCuw2rqP87oLg6PGlkomVDFqw2hIwsSfwWpu4k3XLcBPaKKl6ct/GtL/cwNkgwjV/tc0Mqht3VA==} + '@img/sharp-libvips-linux-riscv64@1.3.3': + resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.3.0': - resolution: {integrity: sha512-9APy+nFWhHS+kzLgWZfLcyrUd7YqnAQVa4BPOo4xkoHpdoktOAPG4cEr9+Jpl0TtqfVmcMJimNL5qNTyyOHZNA==} + '@img/sharp-libvips-linux-s390x@1.3.3': + resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.3.0': - resolution: {integrity: sha512-y9RNUYDe2A1UAdhLyfeOodGRszQdaEoe4nfOpp/sNVPl2CWIcUyFaDoCh4vPLPxu19803j2naLqZup2WxDXCLA==} + '@img/sharp-libvips-linux-x64@1.3.3': + resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.3.0': - resolution: {integrity: sha512-cC1wkC0Mlucd0KSiGrLkJnB/ZqPvZCntc/Lk7ZnYO5ZSbF2euNek4Xvxafojq+wN1q/W0eprdpUIjUr/EV2PBg==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.3.0': - resolution: {integrity: sha512-LiYMhUZicB1QG//+RvmYZpXJO8fYRENfp+MZUCnG9aw+AKvGAy9gPaCnuwsPcBFs8EV66M0NNxj9VHcNklE8zw==} + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.35.0': - resolution: {integrity: sha512-4+4XHLNT5wDT0roYlHTEmH9lDKt0acf9Tv+3hM3iceOirkxrR404/3WjAYZ9F9CkHrxeRcGLJXbi4vluMZ9O+A==} + '@img/sharp-linux-arm64@0.35.4': + resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.35.0': - resolution: {integrity: sha512-VVlpEWwizEFIOom0zdoeKuO5nuTswzVE5uHcBNvHzmeHUpNFajY3HFfbQ+zIH4E2kVaZ/yVxmsShW56TtEy4uA==} + '@img/sharp-linux-arm@0.35.4': + resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.35.0': - resolution: {integrity: sha512-N3hzbEpUTJC8pWpPVJvgzGxM+so/MAXc8O2s/53B0LL9ZGpfXpME7Wizkc5d/8fRBlBtkDjzoZGDCqqNDHqLEw==} + '@img/sharp-linux-ppc64@0.35.4': + resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.35.0': - resolution: {integrity: sha512-l6vmKVPnbS0RhVMbyxP5meAARsbhCnBN4fy31qz0+3a6Rv4jEqfzDrT89y6ZPkCi0AJGnwp2En528yXo401Hpw==} + '@img/sharp-linux-riscv64@0.35.4': + resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.35.0': - resolution: {integrity: sha512-MYlMiPFiv/EKPAHnp3yNZ9AAWFsxga9c5Bkc6wkar6bqzHLlkGVJHRm0u1ei+VXnZxp3Mz9MG9ZIsI8vSOf3sQ==} + '@img/sharp-linux-s390x@0.35.4': + resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.35.0': - resolution: {integrity: sha512-TYaItB5oj1ioXjhyn2xrR208vf+YuIIcHptQWRRaBmFhvIvL9D72DXN8w75xup0KXA8UdEAhQ9Qb2S49FD/9Cw==} + '@img/sharp-linux-x64@0.35.4': + resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.35.0': - resolution: {integrity: sha512-DSTb6ijQzqe6DdAaOBVqJ/SYf1vO8EW5bK6X6LRXufEBebf2722VCdvBUtZ3rtV0x2ApfPNDy/p7LrrjaWjiyQ==} + '@img/sharp-linuxmusl-arm64@0.35.4': + resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.35.0': - resolution: {integrity: sha512-K7ykQ+26Rt6+4BTU80AuGgTPIYX86UxiAKT4rcXX/WNTo7k1ZxpKz+TguHnwVpCqQK3B5PK0vZ0ZBe6nz/ib1w==} + '@img/sharp-linuxmusl-x64@0.35.4': + resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.35.0': - resolution: {integrity: sha512-9woLIFORERCr+6cWu87dQ22J34EExkhc73U1kZW0c+RclQqWetoodByp4dWZ/hN8/KVmTRAx2HOnUwib8AwZdA==} + '@img/sharp-wasm32@0.35.4': + resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} engines: {node: '>=20.9.0'} - '@img/sharp-webcontainers-wasm32@0.35.0': - resolution: {integrity: sha512-t+kie1TOyaDM6Dho+f+y0VqIUNhYQaKCUahuZVi0E0frgdiaOaPsDxDW3wfKacUdaNBCnK/ZDBMg33ydvHj8uA==} + '@img/sharp-webcontainers-wasm32@0.35.4': + resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.35.0': - resolution: {integrity: sha512-M5eKxug0dabbaWgFKvPa3odNs2OpaP+81NASfGKkt4GcYXpNhSu7CaeYxWkLNV6vHmUp4hnCxnxrUyhUJhXbKA==} + '@img/sharp-win32-arm64@0.35.4': + resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.35.0': - resolution: {integrity: sha512-z0+pZ03QCDvdVN0Ez9IX/yjWC19ikMlXrmdYMwYNLTh2BLPx3hXWPvyqWfquZ0BTO9O6GVOjIVoTcyyacMnWlQ==} + '@img/sharp-win32-ia32@0.35.4': + resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.35.0': - resolution: {integrity: sha512-feNnlz5ZHKr0MY1LPHvZQyJeBkbo4ctsn0D8FvA53VTw5TC63rfEL2UrWbkSBR19htSE7Mw78xYVwdJqoMWVHw==} + '@img/sharp-win32-x64@0.35.4': + resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -549,60 +556,60 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@next/env@16.2.12': - resolution: {integrity: sha512-d0Z5Bc13Fa4nR8pFAKx2jay2yhJM16vlfHbTzYnUQAxlNb6B6lmn4hjt69lYNt4kRtyYP6gEM49lPRHNbIyneg==} + '@next/env@16.3.4': + resolution: {integrity: sha512-cjWZnUUa6jZq2kFaNe/ZyJdZonOZ/QoN0Zka2nz/FLOrfx14pQuM9c5RaSVkWMqgdt4ksgPAMWPyHSs/CyV48Q==} - '@next/eslint-plugin-next@16.2.12': - resolution: {integrity: sha512-uF2z/qAK2q7B5/6CpnFcBRX6jOq5iCO+Uqh1UkJhXljX1JwLarLYhhoJadO6dPb6moTprOKewMXheBcbIoSbug==} + '@next/eslint-plugin-next@16.3.4': + resolution: {integrity: sha512-szW9y2Aumu4z88YXfTzcFsgUAg2k64uzbtcO5L9f1AKS4w/GUKJcbFllRflROVyNPgJtGOnvNxiyp3v6b+prIA==} - '@next/swc-darwin-arm64@16.2.12': - resolution: {integrity: sha512-0W1R0teHWJrqKX0FH20IzzIWAOuGtBxPGuObrxy1lE8hQvCFj49KE8a3WUg0D7sq6rn6zkM4c7YGUnhudBS6oA==} + '@next/swc-darwin-arm64@16.3.4': + resolution: {integrity: sha512-iBr3I5LZNk5/bgl5//iTgD2tcym14MX0Xo7fD//u9dYAEgGzza1y9oywluPtf74YnOswVdH1908aK9xVz7zQTw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.12': - resolution: {integrity: sha512-Hy5Ls099+aFUmOLmIgPfLqNi6iCwhL3uQCssz5rWk+5Nkc6TUKCE83DY5BbNylfm3+mfwcSFnLRfrZDJhVxdtw==} + '@next/swc-darwin-x64@16.3.4': + resolution: {integrity: sha512-2dpiSyl2Jw/NrBPaU2MAKGSa+2MR82pJIn4Sm5Rjr+gxAeuh0z158Su3Z2O8zn7UNNq+ej4bToed6RcRN/Lydg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.12': - resolution: {integrity: sha512-+YqU2h1cQkHsGfvjAsrSmst8UIFBibBGm5x3Xgel8NLMiDQtNOM4sM2GOEMvG5YiOBNeN/Ykk8cQC2S0Xrqljg==} + '@next/swc-linux-arm64-gnu@16.3.4': + resolution: {integrity: sha512-+t+U8HZT+fApePCS5h89CSH3datz29MkzyfCn+6fpsZBG/oiEOhINcb9rtkv6sdpToLGFn2e6146NzaKCXkqrA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.12': - resolution: {integrity: sha512-0qjhiYBaKAqF63LA1ZWAAnKTzFUguAaZiRa5etMLGGPj/B6uEVjtIZldIzFEp3wHlB0koK6aTzqPtSdplTCjoA==} + '@next/swc-linux-arm64-musl@16.3.4': + resolution: {integrity: sha512-mx03GNs1ocQA5JQ4FxDMmIsNkdrZh8cuezKCrId28e5/gIPU/l7Kcy2+vmCCzdjnnmXJy+iOAu+7K0QppO6Urg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.12': - resolution: {integrity: sha512-7A3q26W+h7gnA15uqBToNuDqBEFZZcqh0mW2mn4AJh/G5pdg2RVE3n4slzLEliASZFG3NmsbEzng/x2Sh09mBg==} + '@next/swc-linux-x64-gnu@16.3.4': + resolution: {integrity: sha512-YIhGY6fSMfha52bnVxnzc9zaVBzJg+cqQTOD8tXIBSx4fuv0pVMxQTE0PaS59YhnMOiYiG09IMwxJAf/CFm/Dw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.12': - resolution: {integrity: sha512-qSjL/uppm+cbh21s72Ss8gkiOhQ4dExWHNGOWy6eZV7STj5WsKehgxT61beSsOj+YYQuTplL376lOCdMQU5T8w==} + '@next/swc-linux-x64-musl@16.3.4': + resolution: {integrity: sha512-+eaaX6axpDb0yF1GCpiERe6njplvdC+nks/fKfcHu3XPGRrald8P3/X7yv7QLdjA51knnxwl9pxdIJsg+w1L+Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.12': - resolution: {integrity: sha512-X6hzsOUJac/e7AWSbn9gQ9nzHld1xWP5iyjHpYWvud8pufB679O1xg4JDyKr8Xd69Jvd+kM2Der6uftiZCmjYA==} + '@next/swc-win32-arm64-msvc@16.3.4': + resolution: {integrity: sha512-0jcXW7Xs/uzICrmgV3MhDYDeRy++1CqnpDIerlPIqYO4bhzB4WNbX/aRnQclustsAyTkFKB0z6rbcjmNg5tR8A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.12': - resolution: {integrity: sha512-F6fakeHuFTLOPt0bslQJdf+xtT+WIP9DVn/m4y1w1mRnVPyh3D/cNvzlRkxM444xfm+IvvYNSOrKiA2CDJ0Uxw==} + '@next/swc-win32-x64-msvc@16.3.4': + resolution: {integrity: sha512-vvBzwu1pYQCp92maZCFCIw/XgOTMR5tur9GjakwIo2cmwRTMKajRZZDS9+e4KsUZWKu1E007WUeAFXRRjZeuzw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -893,8 +900,8 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} '@tailwindcss/node@4.3.3': resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} @@ -1199,20 +1206,20 @@ packages: cpu: [x64] os: [win32] - '@vitest/coverage-v8@4.1.10': - resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} + '@vitest/coverage-v8@4.1.11': + resolution: {integrity: sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==} peerDependencies: - '@vitest/browser': 4.1.10 - vitest: 4.1.10 + '@vitest/browser': 4.1.11 + vitest: 4.1.11 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.10': - resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} + '@vitest/expect@4.1.11': + resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} - '@vitest/mocker@4.1.10': - resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} + '@vitest/mocker@4.1.11': + resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1222,20 +1229,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.10': - resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + '@vitest/pretty-format@4.1.11': + resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - '@vitest/runner@4.1.10': - resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + '@vitest/runner@4.1.11': + resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} - '@vitest/snapshot@4.1.10': - resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + '@vitest/snapshot@4.1.11': + resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} - '@vitest/spy@4.1.10': - resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + '@vitest/spy@4.1.11': + resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} - '@vitest/utils@4.1.10': - resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + '@vitest/utils@4.1.11': + resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1529,8 +1536,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@16.2.12: - resolution: {integrity: sha512-iaaf4vvKo5h2LBdGt0JuRv7t0Ysqr9FMCiFxbptDg8LqOE//mIKR80DdpOnSVM7qjLH3jT8P0aFiwXxBEGZRXw==} + eslint-config-next@16.3.4: + resolution: {integrity: sha512-35/8RM10huEL9vlr8hUZMERMENHBrnyHN3ZZkF9efSgzGaqK34jIqry44A956//zriUhUAUW0XSkcolhrryqAA==} peerDependencies: eslint: '>=9.0.0' typescript: '>=3.3.1' @@ -1622,6 +1629,7 @@ packages: eslint@9.39.5: resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -1980,8 +1988,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.3.0: - resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true jsdom@30.0.1: @@ -2259,8 +2267,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@16.2.12: - resolution: {integrity: sha512-iD59eYQWmbFcEbX7v/acG5DRym9iw1DdaPoD0WTA920naWsE25wShzJW4+UvAs8MK9EC2kBfIH6vtto1H1PHGw==} + next@16.3.4: + resolution: {integrity: sha512-/Ztf6CeRH+ejEXUrYtqI4gkS66eFIHuSwqi60RgcpWKodxFZx2/dqVCMKBwILfAHXQ+F1b1vAudgj3mnxqtoIA==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -2504,9 +2512,14 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - sharp@0.35.0: - resolution: {integrity: sha512-BqvG5XbwPZ4NV0DK90d86leEECMsoa8bO0nqnKWlBDYxri4GJ7c4EDInaF6q20lTh/mATmnDIKWJFfXnoVfH5g==} + sharp@0.35.4: + resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -2796,20 +2809,20 @@ packages: yaml: optional: true - vitest@4.1.10: - resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} + vitest@4.1.11: + resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.10 - '@vitest/browser-preview': 4.1.10 - '@vitest/browser-webdriverio': 4.1.10 - '@vitest/coverage-istanbul': 4.1.10 - '@vitest/coverage-v8': 4.1.10 - '@vitest/ui': 4.1.10 + '@vitest/browser-playwright': 4.1.11 + '@vitest/browser-preview': 4.1.11 + '@vitest/browser-webdriverio': 4.1.11 + '@vitest/coverage-istanbul': 4.1.11 + '@vitest/coverage-v8': 4.1.11 + '@vitest/ui': 4.1.11 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3130,6 +3143,11 @@ snapshots: eslint: 9.39.5(jiti@2.7.0) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0))': + dependencies: + eslint: 9.39.5(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} '@eslint/config-array@0.21.2': @@ -3148,7 +3166,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.6': + '@eslint/eslintrc@3.3.7': dependencies: ajv: 6.15.0 debug: 4.4.3 @@ -3156,7 +3174,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.3.0 + js-yaml: 4.3.2 minimatch: 3.1.5(patch_hash=5f38b9c5382c1163b0389810f5e4e867519096f3c11a6df0a51d7cafbdfa93e2) strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -3209,108 +3227,108 @@ snapshots: '@img/colour@1.1.0': optional: true - '@img/sharp-darwin-arm64@0.35.0': + '@img/sharp-darwin-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.0 + '@img/sharp-libvips-darwin-arm64': 1.3.3 optional: true - '@img/sharp-darwin-x64@0.35.0': + '@img/sharp-darwin-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.0 + '@img/sharp-libvips-darwin-x64': 1.3.3 optional: true - '@img/sharp-freebsd-wasm32@0.35.0': + '@img/sharp-freebsd-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.0 + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.3.0': + '@img/sharp-libvips-darwin-arm64@1.3.3': optional: true - '@img/sharp-libvips-darwin-x64@1.3.0': + '@img/sharp-libvips-darwin-x64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm64@1.3.0': + '@img/sharp-libvips-linux-arm64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm@1.3.0': + '@img/sharp-libvips-linux-arm@1.3.3': optional: true - '@img/sharp-libvips-linux-ppc64@1.3.0': + '@img/sharp-libvips-linux-ppc64@1.3.3': optional: true - '@img/sharp-libvips-linux-riscv64@1.3.0': + '@img/sharp-libvips-linux-riscv64@1.3.3': optional: true - '@img/sharp-libvips-linux-s390x@1.3.0': + '@img/sharp-libvips-linux-s390x@1.3.3': optional: true - '@img/sharp-libvips-linux-x64@1.3.0': + '@img/sharp-libvips-linux-x64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.0': + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.0': + '@img/sharp-libvips-linuxmusl-x64@1.3.3': optional: true - '@img/sharp-linux-arm64@0.35.0': + '@img/sharp-linux-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.0 + '@img/sharp-libvips-linux-arm64': 1.3.3 optional: true - '@img/sharp-linux-arm@0.35.0': + '@img/sharp-linux-arm@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.0 + '@img/sharp-libvips-linux-arm': 1.3.3 optional: true - '@img/sharp-linux-ppc64@0.35.0': + '@img/sharp-linux-ppc64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.0 + '@img/sharp-libvips-linux-ppc64': 1.3.3 optional: true - '@img/sharp-linux-riscv64@0.35.0': + '@img/sharp-linux-riscv64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.0 + '@img/sharp-libvips-linux-riscv64': 1.3.3 optional: true - '@img/sharp-linux-s390x@0.35.0': + '@img/sharp-linux-s390x@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.0 + '@img/sharp-libvips-linux-s390x': 1.3.3 optional: true - '@img/sharp-linux-x64@0.35.0': + '@img/sharp-linux-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.0 + '@img/sharp-libvips-linux-x64': 1.3.3 optional: true - '@img/sharp-linuxmusl-arm64@0.35.0': + '@img/sharp-linuxmusl-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 optional: true - '@img/sharp-linuxmusl-x64@0.35.0': + '@img/sharp-linuxmusl-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.0 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 optional: true - '@img/sharp-wasm32@0.35.0': + '@img/sharp-wasm32@0.35.4': dependencies: '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-webcontainers-wasm32@0.35.0': + '@img/sharp-webcontainers-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.0 + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-win32-arm64@0.35.0': + '@img/sharp-win32-arm64@0.35.4': optional: true - '@img/sharp-win32-ia32@0.35.0': + '@img/sharp-win32-ia32@0.35.4': optional: true - '@img/sharp-win32-x64@0.35.0': + '@img/sharp-win32-x64@0.35.4': optional: true '@jridgewell/gen-mapping@0.3.13': @@ -3346,34 +3364,37 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true - '@next/env@16.2.12': {} + '@next/env@16.3.4': {} - '@next/eslint-plugin-next@16.2.12': + '@next/eslint-plugin-next@16.3.4(eslint@9.39.5(jiti@2.7.0))': dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) fast-glob: 3.3.1 + transitivePeerDependencies: + - eslint - '@next/swc-darwin-arm64@16.2.12': + '@next/swc-darwin-arm64@16.3.4': optional: true - '@next/swc-darwin-x64@16.2.12': + '@next/swc-darwin-x64@16.3.4': optional: true - '@next/swc-linux-arm64-gnu@16.2.12': + '@next/swc-linux-arm64-gnu@16.3.4': optional: true - '@next/swc-linux-arm64-musl@16.2.12': + '@next/swc-linux-arm64-musl@16.3.4': optional: true - '@next/swc-linux-x64-gnu@16.2.12': + '@next/swc-linux-x64-gnu@16.3.4': optional: true - '@next/swc-linux-x64-musl@16.2.12': + '@next/swc-linux-x64-musl@16.3.4': optional: true - '@next/swc-win32-arm64-msvc@16.2.12': + '@next/swc-win32-arm64-msvc@16.3.4': optional: true - '@next/swc-win32-x64-msvc@16.2.12': + '@next/swc-win32-x64-msvc@16.3.4': optional: true '@nodelib/fs.scandir@2.1.5': @@ -3584,7 +3605,7 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@swc/helpers@0.5.15': + '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 @@ -3850,10 +3871,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true - '@vitest/coverage-v8@4.1.10(vitest@4.1.10)': + '@vitest/coverage-v8@4.1.11(vitest@4.1.11)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.10 + '@vitest/utils': 4.1.11 ast-v8-to-istanbul: 1.0.4 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 @@ -3862,46 +3883,46 @@ snapshots: obug: 2.1.3 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1)(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)) + vitest: 4.1.11(@types/node@26.1.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1)(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)) - '@vitest/expect@4.1.10': + '@vitest/expect@4.1.11': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.10 - '@vitest/utils': 4.1.10 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.10(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0))': + '@vitest/mocker@4.1.11(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0))': dependencies: - '@vitest/spy': 4.1.10 + '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 8.1.4(@types/node@26.1.2)(jiti@2.7.0) - '@vitest/pretty-format@4.1.10': + '@vitest/pretty-format@4.1.11': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.10': + '@vitest/runner@4.1.11': dependencies: - '@vitest/utils': 4.1.10 + '@vitest/utils': 4.1.11 pathe: 2.0.3 - '@vitest/snapshot@4.1.10': + '@vitest/snapshot@4.1.11': dependencies: - '@vitest/pretty-format': 4.1.10 - '@vitest/utils': 4.1.10 + '@vitest/pretty-format': 4.1.11 + '@vitest/utils': 4.1.11 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.10': {} + '@vitest/spy@4.1.11': {} - '@vitest/utils@4.1.10': + '@vitest/utils@4.1.11': dependencies: - '@vitest/pretty-format': 4.1.10 + '@vitest/pretty-format': 4.1.11 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -4287,12 +4308,12 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@16.2.12(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): + eslint-config-next@16.3.4(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@next/eslint-plugin-next': 16.2.12 + '@next/eslint-plugin-next': 16.3.4(eslint@9.39.5(jiti@2.7.0)) eslint: 9.39.5(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)) @@ -4315,7 +4336,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -4330,14 +4351,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) eslint: 9.39.5(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) transitivePeerDependencies: - supports-color @@ -4352,7 +4373,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.5(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) hasown: 2.0.4 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -4440,7 +4461,7 @@ snapshots: '@eslint/config-array': 0.21.2 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.6 + '@eslint/eslintrc': 3.3.7 '@eslint/js': 9.39.5 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 @@ -4826,7 +4847,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.3.0: + js-yaml@4.3.2: dependencies: argparse: 2.0.1 @@ -5055,10 +5076,10 @@ snapshots: natural-compare@1.4.0: {} - next@16.2.12(@babel/core@7.29.7)(@playwright/test@1.62.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + next@16.3.4(@babel/core@7.29.7)(@playwright/test@1.62.0)(@types/node@26.1.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@next/env': 16.2.12 - '@swc/helpers': 0.5.15 + '@next/env': 16.3.4 + '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.11.5 caniuse-lite: 1.0.30001806 postcss: 8.5.24 @@ -5066,18 +5087,19 @@ snapshots: react-dom: 19.2.8(react@19.2.8) styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.8) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.12 - '@next/swc-darwin-x64': 16.2.12 - '@next/swc-linux-arm64-gnu': 16.2.12 - '@next/swc-linux-arm64-musl': 16.2.12 - '@next/swc-linux-x64-gnu': 16.2.12 - '@next/swc-linux-x64-musl': 16.2.12 - '@next/swc-win32-arm64-msvc': 16.2.12 - '@next/swc-win32-x64-msvc': 16.2.12 + '@next/swc-darwin-arm64': 16.3.4 + '@next/swc-darwin-x64': 16.3.4 + '@next/swc-linux-arm64-gnu': 16.3.4 + '@next/swc-linux-arm64-musl': 16.3.4 + '@next/swc-linux-x64-gnu': 16.3.4 + '@next/swc-linux-x64-musl': 16.3.4 + '@next/swc-win32-arm64-msvc': 16.3.4 + '@next/swc-win32-x64-msvc': 16.3.4 '@playwright/test': 1.62.0 - sharp: 0.35.0 + sharp: 0.35.4(@types/node@26.1.2) transitivePeerDependencies: - '@babel/core' + - '@types/node' - babel-plugin-macros node-exports-info@1.6.2: @@ -5338,37 +5360,38 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 - sharp@0.35.0: + sharp@0.35.4(@types/node@26.1.2): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.0 - '@img/sharp-darwin-x64': 0.35.0 - '@img/sharp-freebsd-wasm32': 0.35.0 - '@img/sharp-libvips-darwin-arm64': 1.3.0 - '@img/sharp-libvips-darwin-x64': 1.3.0 - '@img/sharp-libvips-linux-arm': 1.3.0 - '@img/sharp-libvips-linux-arm64': 1.3.0 - '@img/sharp-libvips-linux-ppc64': 1.3.0 - '@img/sharp-libvips-linux-riscv64': 1.3.0 - '@img/sharp-libvips-linux-s390x': 1.3.0 - '@img/sharp-libvips-linux-x64': 1.3.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.0 - '@img/sharp-libvips-linuxmusl-x64': 1.3.0 - '@img/sharp-linux-arm': 0.35.0 - '@img/sharp-linux-arm64': 0.35.0 - '@img/sharp-linux-ppc64': 0.35.0 - '@img/sharp-linux-riscv64': 0.35.0 - '@img/sharp-linux-s390x': 0.35.0 - '@img/sharp-linux-x64': 0.35.0 - '@img/sharp-linuxmusl-arm64': 0.35.0 - '@img/sharp-linuxmusl-x64': 0.35.0 - '@img/sharp-webcontainers-wasm32': 0.35.0 - '@img/sharp-win32-arm64': 0.35.0 - '@img/sharp-win32-ia32': 0.35.0 - '@img/sharp-win32-x64': 0.35.0 + '@img/sharp-darwin-arm64': 0.35.4 + '@img/sharp-darwin-x64': 0.35.4 + '@img/sharp-freebsd-wasm32': 0.35.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-linux-arm': 0.35.4 + '@img/sharp-linux-arm64': 0.35.4 + '@img/sharp-linux-ppc64': 0.35.4 + '@img/sharp-linux-riscv64': 0.35.4 + '@img/sharp-linux-s390x': 0.35.4 + '@img/sharp-linux-x64': 0.35.4 + '@img/sharp-linuxmusl-arm64': 0.35.4 + '@img/sharp-linuxmusl-x64': 0.35.4 + '@img/sharp-webcontainers-wasm32': 0.35.4 + '@img/sharp-win32-arm64': 0.35.4 + '@img/sharp-win32-ia32': 0.35.4 + '@img/sharp-win32-x64': 0.35.4 + '@types/node': 26.1.2 optional: true shebang-command@2.0.0: @@ -5675,15 +5698,15 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 - vitest@4.1.10(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1)(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)): + vitest@4.1.11(@types/node@26.1.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1)(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)): dependencies: - '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)) - '@vitest/pretty-format': 4.1.10 - '@vitest/runner': 4.1.10 - '@vitest/snapshot': 4.1.10 - '@vitest/spy': 4.1.10 - '@vitest/utils': 4.1.10 + '@vitest/expect': 4.1.11 + '@vitest/mocker': 4.1.11(vite@8.1.4(@types/node@26.1.2)(jiti@2.7.0)) + '@vitest/pretty-format': 4.1.11 + '@vitest/runner': 4.1.11 + '@vitest/snapshot': 4.1.11 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 es-module-lexer: 2.3.0 expect-type: 1.4.0 magic-string: 0.30.21 @@ -5699,7 +5722,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.1.2 - '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) + '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) jsdom: 30.0.1 transitivePeerDependencies: - msw diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml index d028031d2..70f2c4eea 100644 --- a/frontend/pnpm-workspace.yaml +++ b/frontend/pnpm-workspace.yaml @@ -15,8 +15,9 @@ supportedArchitectures: overrides: brace-expansion: "5.0.9" + js-yaml: "4.3.2" postcss: "8.5.24" - sharp: "0.35.0" + sharp: "0.35.4" undici: 8.9.0 patchedDependencies: diff --git a/opencode.jsonc b/opencode.jsonc deleted file mode 100644 index 536d212fb..000000000 --- a/opencode.jsonc +++ /dev/null @@ -1,77 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "model": "github-models/openai/gpt-5", - "small_model": "github-models/deepseek/deepseek-v3-0324", - "enabled_providers": ["github-models"], - "mcp": { - "codegraph": { - "type": "local", - "command": ["npx", "-y", "@colbymchenry/codegraph@0.9.9", "serve", "--mcp"], - "enabled": true - }, - "deepwiki": { - "type": "remote", - "url": "https://mcp.deepwiki.com/mcp", - "enabled": true, - "timeout": 300000 - }, - "context7": { - "type": "local", - "command": ["npx", "-y", "@upstash/context7-mcp@3.1.0", "--transport", "stdio"], - "enabled": true, - "timeout": 300000, - "environment": { - "NPM_CONFIG_IGNORE_SCRIPTS": "true", - "NPM_CONFIG_LOGLEVEL": "error" - } - }, - "web_search": { - "type": "local", - "command": ["npx", "-y", "@guhcostan/web-search-mcp@1.0.5"], - "enabled": true, - "timeout": 300000, - "environment": { - "NPM_CONFIG_IGNORE_SCRIPTS": "true", - "NPM_CONFIG_LOGLEVEL": "error" - } - } - }, - "provider": { - "github-models": { - "npm": "@ai-sdk/openai-compatible", - "name": "GitHub Models", - "options": { - "baseURL": "https://models.github.ai/inference", - "apiKey": "{env:STRIX_GITHUB_MODELS_TOKEN}" - }, - "models": { - "openai/gpt-5": { - "name": "OpenAI GPT-5", - "tool_call": true, - "reasoning": true, - "limit": { - "context": 200000, - "output": 100000 - } - }, - "deepseek/deepseek-r1-0528": { - "name": "DeepSeek R1 0528", - "tool_call": true, - "reasoning": true, - "limit": { - "context": 128000, - "output": 4096 - } - }, - "deepseek/deepseek-v3-0324": { - "name": "DeepSeek V3 0324", - "tool_call": true, - "limit": { - "context": 128000, - "output": 4096 - } - } - } - } - } -}