diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a0a855b
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,58 @@
+name: CI
+
+on:
+ push:
+ branches: ["**"]
+ pull_request:
+
+jobs:
+ quality:
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ - name: Enable corepack
+ run: corepack enable
+ - name: Bootstrap
+ run: corepack pnpm bootstrap
+ - name: Lint
+ run: corepack pnpm lint
+ - name: Typecheck
+ run: corepack pnpm typecheck
+ - name: Test
+ run: corepack pnpm test
+
+ demo_verify:
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ - name: Enable corepack
+ run: corepack enable
+ - name: Bootstrap
+ run: corepack pnpm bootstrap
+ - name: Demo verify
+ run: corepack pnpm demo:verify
+
+ extension_smoke:
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ - name: Enable corepack
+ run: corepack enable
+ - name: Bootstrap
+ run: corepack pnpm bootstrap
+ - name: Install Playwright Chromium
+ run: corepack pnpm exec playwright install --with-deps chromium
+ - name: Extension smoke
+ run: xvfb-run -a corepack pnpm test:extension-smoke
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..93148ab
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+node_modules/
+.pnpm-store/
+apps/**/node_modules/
+apps/**/.next/
+apps/**/build/
+apps/**/dist/
+apps/extension/public/manifest.json
+packages/**/node_modules/
+packages/**/dist/
+packages/**/tsconfig.tsbuildinfo
+apps/**/tsconfig.tsbuildinfo
+*.log
+.DS_Store
+.tmp-extension-smoke-profile/
+playwright-report/
+test-results/
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..44a6d1f
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,6 @@
+{
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "all",
+ "printWidth": 100
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c8635e5
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,43 @@
+# CHANGELOG
+
+## Unreleased
+
+### Added
+
+---
+
+## v0.214 — 2026-02-14
+
+### Added
+
+- `CODE_OF_CONDUCT.md` for community standards.
+- Structured extension verifier inspector UI with grouped protocol sections.
+- Expandable raw-response viewer showing verbatim server payload.
+- Extension runtime `GBV_PING` message for deterministic smoke checks.
+- Shared server logger with request IDs and stage timing.
+- Privacy-safe trace metadata in verifier responses (`requestId`, `timing`, `traceSummary`, `failedInvariantIds`).
+- Top-level `/docs` structure:
+ - `docs/architecture.md`
+ - `docs/protocol-overview.md`
+ - `docs/threat-model.md`
+ - `docs/demo-walkthrough.md`
+ - `docs/research/README.md`
+- `@gbv/core` unit tests for receipts, merkle proofs, and structure-bound nonces.
+- Playwright-based extension smoke script (`scripts/extension-smoke.ts`).
+- GitHub Actions CI workflow with quality, strict demo, and extension smoke jobs.
+- Release hardening report (`docs/release-hardening-report.md`).
+
+### Changed
+
+- `demo:verify` now performs strict assertions for baseline/adversarial outcomes.
+- Extension and server debug diagnostics gated by `GBV_DEBUG`.
+- `README.md` rewritten for onboarding-first OSS usage.
+- `SECURITY.md` moved to GitHub Security Advisories reporting.
+- Development and traceability docs consolidated under `/docs`.
+
+---
+
+## 1.0.0
+
+- GBV workspace architecture with server, synthetic client, extension, and shared core packages.
+- Config-driven GBV API routes and deterministic synthetic dataset outcomes.
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..524ba09
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for
+moderation decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official email address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported through GitHub Security Advisories private reporting in this
+repository's **Security** tab. All complaints will be reviewed and investigated
+promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
+
+[homepage]: https://www.contributor-covenant.org
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..1d57fd6
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,113 @@
+# CONTRIBUTING
+
+Thank you for your interest in contributing to the Glass Ballroom Verification (GBV) reference implementation.
+
+This repository is a **protocol reference and evaluation environment**, so contributions should prioritize correctness, reproducibility, and architectural clarity over feature expansion.
+
+---
+
+## Local Setup
+
+Prepare a development environment:
+
+```bash
+corepack prepare pnpm@10.4.1 --activate
+corepack pnpm bootstrap
+corepack pnpm dev
+```
+
+This will install workspace dependencies, build required artifacts, and start the local development environment.
+
+---
+
+## Required Checks Before Opening a Pull Request
+
+All contributions must pass the following checks:
+
+```bash
+corepack pnpm lint
+corepack pnpm typecheck
+corepack pnpm test
+corepack pnpm demo:verify
+```
+
+These ensure code quality, type safety, protocol stability, and expected verifier behavior.
+
+### Recommended Targeted Checks
+
+Depending on the area of change, contributors are encouraged to run:
+
+```bash
+corepack pnpm test:server
+corepack pnpm test:attack
+corepack pnpm build:extension
+```
+
+These help validate API behavior, adversarial regression coverage, and extension compatibility.
+
+---
+
+## Contribution Rules
+
+To preserve protocol correctness and evaluation integrity:
+
+- Maintain alignment with the GBV specification defined in **`gbv_v0.71.pdf`**.
+- Preserve the **blind verification property** — the server must not encode dataset validity labels.
+- Do not introduce authentication systems, external APIs, or secret-dependent flows.
+- Treat `gbv.config.ts` as the single source of runtime configuration.
+- Reuse shared protocol logic from `@gbv/core`; avoid duplicating verification behavior across components.
+- Prefer deterministic behavior over environment-dependent logic.
+
+Changes that alter protocol semantics should be clearly justified and documented.
+
+---
+
+## Documentation Expectations
+
+Documentation must be updated when behavior or structure changes.
+
+| File | When to Update |
+| ---------------------- | -------------------------------------- |
+| `README.md` | Setup, demo workflow, or usage changes |
+| `docs/protocol-overview.md` | Specification-to-code mapping changes |
+| `docs/architecture.md` | Structural or architectural decisions |
+| `docs/threat-model.md` | Threat-model or adversarial assumption updates |
+| `docs/demo-walkthrough.md` | Demo workflow or inspection UX changes |
+| `CHANGELOG.md` | User-visible or release-facing changes |
+
+Documentation updates are considered part of the contribution, not optional follow-up work.
+
+---
+
+## Pull Request Requirements
+
+Each pull request should include:
+
+- a concise summary of what changed
+- a **protocol impact note** (if applicable)
+- evidence of successful testing (commands run and outcomes)
+
+Example:
+
+```
+Ran:
+- pnpm lint
+- pnpm test
+- pnpm demo:verify
+
+Result:
+All checks passed. No protocol behavior changes.
+```
+
+---
+
+## Contribution Philosophy
+
+GBV is a protocol reference implementation, not a feature-driven application. Contributions should aim to:
+
+- improve correctness
+- strengthen reproducibility
+- clarify implementation intent
+- maintain architectural simplicity
+
+When in doubt, prefer smaller, well-scoped changes that preserve protocol transparency.
diff --git a/README.md b/README.md
index 2c952ec..be6e681 100644
--- a/README.md
+++ b/README.md
@@ -1,56 +1,216 @@
-# ARGON-V
+# Glass Ballroom Verification (GBV) Reference Implementation
-**Adversarial-Resistant Ground-truth ObservatioN - Verification**
+**Glass Ballroom Verification (GBV)** is a deterministic client-server verification protocol for evaluating cross-surface semantic consistency of browser-observable artifacts under verifier authority.
-## Operational Status
+This repository provides the official GBV reference implementation. GBV is domain-agnostic; course records are included only as a reproducible demonstration environment.
-**Current phase:** Active development with protocol hardening (v0.1).
-
-This repository serves as the canonical home for the **ARGON-V** protocol. Development is actively underway, with parallel progress on implementation and formalization. Current work focuses on refining protocol invariants, expanding threat-surface coverage, and validating design assumptions against realistic adversarial models.
+> **Documentation state — v0.214**
+>
+> This repository reflects the state of the GBV reference implementation
+> as of February 14, 2026, when protocol terminology was consolidated
+> under the Glass Ballroom Verification (GBV) name. Earlier drafts may
+> reference ARGON-V terminology.
> [!NOTE]
> While core protocol mechanics are implemented, certain components remain under iterative refinement as security assumptions and edge cases are stress-tested. Interfaces and internal representations may evolve prior to the v0.1 stabilization milestone.
-## The Economic Model of Trust
+## What This Repository Provides
+
+- A deterministic GBV reference environment for engineers and researchers.
+- A complete client-server verification flow:
+ - MV3 browser extension collects browser-observable surfaces.
+ - Server independently evaluates protocol invariants.
+- A transparency-first verification inspector with structured summaries and verbatim server payloads.
+
+GBV evaluates whether independently observed surfaces can represent a single coherent state without requiring privileged provider access or shared ground truth.
+
+---
+
+## What This Repository Is Not
+
+- Not a production credential verification service.
+- Not a hosted SaaS deployment.
+- Not a provider-integrated system with authentication, rate limiting, or tenant isolation.
+
+This repository is a protocol reference and research implementation.
+
+---
+
+## Protocol Blindness Clarification
+
+In GBV, blindness means authority separation, not UI opacity.
+
+- The server is decision-authoritative.
+- The server does not rely on dataset validity labels.
+- The client cannot influence verifier outcomes.
+
+The extension can display full server-returned metadata without violating protocol blindness.
+
+---
+
+## Repository Layout
+
+- [`apps/server`](./apps/server/) - GBV verifier API (`/api/gbv/*`)
+- [`apps/client/synthetic`](./apps/client/synthetic/) - synthetic learning platform surfaces
+- [`apps/extension`](./apps/extension/) - Chrome MV3 GBV client
+- [`packages/gbv-core`](./packages/gbv-core/) - canonicalization, invariants, commitments, receipts
+- [`packages/gbv-config`](./packages/gbv-config/) - shared runtime configuration
+- [`docs/`](./docs/) - architecture, protocol, threat model, walkthroughs, and reports
+
+---
+
+## Research Paper Versioning
+
+Research drafts are versioned independently from implementation code.
+
+Current public research draft:
+
+- [`docs/research/gbv_public_v0.214.pdf`](./docs/research/gbv_public_v0.214.pdf)
+
+---
+
+## Prerequisites
+
+- Node.js 22+
+- Corepack
+
+```bash
+corepack enable
+corepack prepare pnpm@10.4.1 --activate
+```
+
+---
+
+## Quickstart
+
+```bash
+corepack pnpm bootstrap
+corepack pnpm dev
+```
+
+Services:
+
+- Synthetic client: [http://localhost:3000](http://localhost:3000)
+- GBV server: [http://localhost:3001](http://localhost:3001)
+- Extension build: [`apps/extension/build`](./apps/extension/build/)
+
+---
+
+## Manual Demo Walkthrough
+
+1. Open `chrome://extensions`.
+2. Enable **Developer Mode**.
+3. Load unpacked extension from [`apps/extension/build`](./apps/extension/build/).
+4. Open `http://localhost:3000/hub`.
+5. Open the extension popup.
+6. Choose a course.
+7. Click **Verify**.
-ARGON-V does not attempt to provide a mathematical guarantee of absolute truth, which is formally impossible in an unmanaged client environment. Instead, it enforces **economic cost asymmetry**.
+### Expected Baseline Behavior
-By requiring simultaneous multi-surface semantic consistency—tied to ephemeral, server-issued challenges—the protocol raises the complexity and labor cost of a successful forgery beyond the expected utility of the exploit. In effect, ARGON-V transforms a _data manipulation_ problem into a _coherent narrative forgery_ problem, which is significantly harder to automate and sustain.
+- `csk_7r2q9p` -> accepted
+- `csk_0a81lm` -> semantic mismatch (`SEMANTIC_VERIFICATION_FAILED`, `module_count_consistency`)
+- `csk_3z19tt` -> semantic mismatch (`SEMANTIC_VERIFICATION_FAILED`, `certificate_id_consistency`)
+- `csk_t1mix` -> semantic mismatch (`SEMANTIC_VERIFICATION_FAILED`, includes `grade_threshold` and `course_key_consistency`)
+
+### Popup Behavior
+
+- Structured summary first (state, status, score, IDs, timing)
+- Grouped protocol sections
+- Expandable **Raw Response** with verbatim verifier payload
---
-## Core System Invariants
+## Automated Verification
+
+```bash
+corepack pnpm lint
+corepack pnpm typecheck
+corepack pnpm test
+corepack pnpm demo:verify
+```
+
+`demo:verify` validates expected protocol states, mismatch classes, and invariant behavior.
-The ARGON-V pipeline enforces three primary safety properties:
+---
-- **Temporal Freshness**
- Observations are cryptographically bound to an ephemeral, high-entropy nonce to prevent replay and pre-computed state injection.
+## Extension Smoke Test
-- **Semantic Coherence**
- Facts are extracted from multiple independent observation surfaces. Verification requires that all surfaces maintain logical consistency (e.g., a dashboard total must equal the sum of its ledger entries).
+```bash
+corepack pnpm exec playwright install chromium
+corepack pnpm test:extension-smoke
+```
-- **Deterministic Canonicalization**
- Raw DOM artifacts are mapped into a strictly typed, server-authoritative schema, neutralizing client-side heuristic manipulation and UI noise.
+CI executes these tests under Xvfb.
---
-## Repository Structure
+## Debug Logs (Off by Default)
+
+PowerShell:
+
+```powershell
+$env:GBV_DEBUG='1'; corepack pnpm dev
+```
+
+Bash:
-- **`apps/server`** — Authoritative verification and Merkle-commitment engine.
-- **`apps/client`** — Core client suite, including the testing DOM (`mock-pass`, `mock-fail`) and the browser extension for server interaction and verification.
-- **`docs/theory`** — Systems analysis and economic-security reasoning. Includes the [research paper](docs/theory/research-paper-v0.1.pdf).
+```bash
+GBV_DEBUG=1 corepack pnpm dev
+```
---
-### Ongoing Work
+## Security and Privacy Notes
+
+- Server logs request IDs, timing metrics, and non-sensitive diagnostics only.
+- Raw page HTML is not logged by default.
+- Security disclosures should use GitHub Security Advisories private reporting.
+
+---
+
+## Troubleshooting
+
+**Extension cannot verify**
+
+- Run `corepack pnpm build:extension`
+- Reload unpacked extension
+- Verify `http://localhost:3001/api/health`
+- Verify `http://localhost:3000/api/gbv/courses`
+
+**Extension smoke test fails**
+
+```bash
+corepack pnpm exec playwright install chromium
+```
+
+---
+
+## Documentation
+
+For structured documentation navigation, start with
+[`docs/README.md`](./docs/README.md).
+
+- [`docs/architecture.md`](./docs/architecture.md)
+- [`docs/protocol-overview.md`](./docs/protocol-overview.md)
+- [`docs/threat-model.md`](./docs/threat-model.md)
+- [`docs/demo-walkthrough.md`](./docs/demo-walkthrough.md)
+- [`docs/release-hardening-report.md`](./docs/release-hardening-report.md)
+- [`docs/development-notes.md`](./docs/development-notes.md)
+- [`docs/traceability.md`](./docs/traceability.md)
+- [`docs/research/`](./docs/research/)
+
+---
-- **Active Development:** Real-time server and client progress is visible on the [**`develop` branch**](../../tree/develop).
-- **Theory & Research:** The current protocol draft and threat model are documented in the [research-paper-v0.1.pdf](docs/theory/research-paper-v0.1.pdf).
+## Project Policies
-Readers interested in implementation progress, design decisions, or protocol mechanics are encouraged to consult the **develop** branch and accompanying theory documentation.
+- [`SECURITY.md`](./SECURITY.md)
+- [`CONTRIBUTING.md`](./CONTRIBUTING.md)
+- [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md)
+- [`CHANGELOG.md`](./CHANGELOG.md)
---
## License
-Licensed under the Apache License, Version 2.0.
+See [`LICENSE`](./LICENSE).
diff --git a/SECURITY.md b/SECURITY.md
index 1441f7c..39c2a6b 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,52 +1,45 @@
# Security Policy
-## Responsible Disclosure
-
-ARGON-V is an adversarial-resistant protocol, and security and integrity are treated as first-class design goals. We welcome and appreciate responsible disclosure from security researchers, protocol analysts, and systems engineers.
-
-At this stage, ARGON-V is an early protocol and systems design (v0.1). Security reports may concern logical flaws, economic attacks, invariant violations, or weaknesses in the formal specification, in addition to issues in any reference implementations.
-
----
-
## Reporting a Vulnerability
-If you identify a potential security issue in the ARGON-V protocol specification or its reference implementations, please do **not** use the public issue tracker.
-
-Instead, report the issue privately to the maintainer:
-
-**Reporting channel:** dante@youroasis.ai
+Do not open public issues for security vulnerabilities.
-Please include, where applicable:
+Use GitHub Security Advisories private reporting:
-- A clear description of the issue or vulnerability
-- The affected protocol stage (e.g., Stage III: Semantic Intersection)
-- A proof-of-concept, attack sketch, or theoretical walkthrough
-- Potential impact on correctness, security, or economic guarantees
-- Suggested mitigations or design alternatives (if available)
+1. Open the repository **Security** tab.
+2. Select **Report a vulnerability**.
+3. Submit a private advisory with details.
----
+## What to Include
-## Scope of Security Review
+- clear vulnerability description
+- affected GBV stage/component
+- reproducible steps
+- expected attacker model/capability
+- observed impact
+- optional mitigation proposal
-During the current **Pre-Birth / Specification Drafting** phase, we are particularly interested in reports covering:
+## Priority Scope
-- **Logical flaws** — Bypasses or inconsistencies in the five-stage pipeline
-- **Economic attacks** — Techniques that reduce the cost of forgery without violating semantic coherence
-- **Protocol invariants** — Contradictions or weaknesses in the formal specification (`SPEC.md`)
-- **Adversarial assumptions** — Gaps or unrealistic constraints in the threat model
+Highest priority findings include:
----
+- nonce-binding bypasses
+- semantic invariant bypasses
+- verifier authority/blindness violations
+- receipt/commitment integrity flaws
+- extension collection flow bypasses
-## Our Commitment
+## Response Expectations
-If you follow the responsible disclosure process outlined above, we commit to:
+- acknowledgement target: 72 hours
+- triage and reproduction
+- coordinated remediation
+- optional attribution upon fix (on request)
-- Acknowledging receipt of your report within **48–72 hours**
-- Working with you to understand, validate, and reproduce the issue
-- Crediting you in security advisories or specification revisions, where appropriate
+## Safe Harbor
----
+Good-faith testing and responsible private disclosure are welcome.
-## Non-Disclosure Policy
+## Scope Context
-We ask that reported issues are not disclosed publicly until they have been addressed in the specification or resolved in a subsequent protocol revision. This policy is intended to support responsible discussion during early development and does not preclude future public disclosure once the protocol reaches a stable release.
+This repository is a local-first protocol reference implementation. Reports should prioritize protocol correctness and verification integrity over hosted production hardening concerns.
diff --git a/SPEC.md b/SPEC.md
deleted file mode 100644
index 80a799a..0000000
--- a/SPEC.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# ARGON-V Protocol Specification (v0.1-draft)
-
-## 1. Introduction
-The ARGON-V protocol defines a method for a Verifier ($V$) to obtain a high-confidence attestation of state $\sigma$ from a third-party Platform (hereafter referred to as the **Source** $\mathcal{S}$) via an untrusted Prover $\mathcal{A}$ (the adversarial client). The protocol operates under the assumption that $\mathcal{A}$ has full control over the local execution environment and may attempt to present a forged state.
-
-## 2. Protocol Invariants
-For any attestation to be considered valid, it must satisfy the following invariants:
-
-1. **Freshness**: The observation must be cryptographically bound to an ephemeral, server-issued nonce $\eta$.
-2. **Coherence**: The set of extracted facts $F = \{f_1, f_2, ..., f_k\}$ across $k$ surfaces must maintain logical consistency. Coherence is evaluated against verifier-defined semantic constraints $\mathcal{C}$, which are necessarily incomplete and platform-specific.
-3. **Immutability**: Once an observation is canonicalized, its Merkle root $R$ must be signed by $V$ to prevent post-facto tampering.
-
-## 3. The Five-Stage Pipeline
-
-### Stage I: Challenge (Temporal Binding)
-The Verifier issues a single-use, high-entropy nonce $\eta$. This nonce must be embedded into the observation environment to prove the observation occurred *after* $\eta$ was issued and within the validity window $\Delta t$.
-
-### Stage II: Observation (Multi-Surface Capture)
-The Prover $\mathcal{A}$ captures $k$ independent surfaces from the Source $\mathcal{S}$. Each surface $s_i \in \{s_1, ..., s_k\}$ contributes to the total evidence pool.
-
-### Stage III: Consistency (Semantic Validation)
-The system evaluates the captured surfaces for cross-surface invariants. If surface $s_1$ represents a summary and $s_2$ represents a detailed ledger, the protocol enforces that $s_1 \equiv s_2$ according to the verifier-defined constraints $\mathcal{C}$.
-
-
-
-### Stage IV: Canonicalization (Deterministic Mapping)
-Raw captured data is stripped of non-deterministic artifacts (e.g., timestamps, session IDs) and mapped into a strictly typed schema. This ensures that equivalent platform states deterministically map to identical cryptographic representations.
-
-### Stage V: Verification (Finality)
-The canonical state is inserted into a Merkle-based accumulator. The Verifier signs the Merkle root $R$, issuing a "Verifiable Attestation" that can be audited by third parties—provided they trust $V$’s public key and the constraint set $\mathcal{C}$—without re-running the extraction pipeline.
-
-## 4. Error States and Termination
-If any invariant is violated during Stages I-III, the protocol must terminate immediately with a `NULL_COMMITMENT`. The protocol prioritizes **Safety** (preventing false positives) over **Liveness** (guaranteeing a result).
diff --git a/apps/client/synthetic/.gitignore b/apps/client/synthetic/.gitignore
new file mode 100644
index 0000000..5ef6a52
--- /dev/null
+++ b/apps/client/synthetic/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/apps/client/synthetic/README.md b/apps/client/synthetic/README.md
new file mode 100644
index 0000000..6b67493
--- /dev/null
+++ b/apps/client/synthetic/README.md
@@ -0,0 +1,27 @@
+# @gbv/synthetic-client
+
+Synthetic multi-surface platform used for GBV verification demos.
+
+## Purpose
+
+- Exposes multiple course artifacts through identical route/API shapes.
+- Includes coherent and adversarial artifacts.
+- Adversarial outcomes emerge from cross-surface inconsistencies, not explicit fail flags.
+
+## Notable Artifact Keys
+
+- `csk_7r2q9p`: coherent baseline artifact (expected GBV acceptance)
+- `csk_t1mix`: visible Tier-1 mixed adversarial artifact (expected GBV rejection)
+
+## Local Commands
+
+From repo root:
+
+- `corepack pnpm --filter @gbv/synthetic-client dev`
+- `corepack pnpm --filter @gbv/synthetic-client lint`
+- `corepack pnpm --filter @gbv/synthetic-client typecheck`
+
+Route catalog API:
+- `GET /api/gbv/courses`
+
+See root `README.md` for extension-driven verification walkthrough.
diff --git a/apps/client/synthetic/eslint.config.mjs b/apps/client/synthetic/eslint.config.mjs
new file mode 100644
index 0000000..f443835
--- /dev/null
+++ b/apps/client/synthetic/eslint.config.mjs
@@ -0,0 +1,16 @@
+import { defineConfig, globalIgnores } from "eslint/config";
+import nextVitals from "eslint-config-next/core-web-vitals";
+
+const eslintConfig = defineConfig([
+ ...nextVitals,
+ // Override default ignores of eslint-config-next.
+ globalIgnores([
+ // Default ignores of eslint-config-next:
+ ".next/**",
+ "out/**",
+ "build/**",
+ "next-env.d.ts",
+ ]),
+]);
+
+export default eslintConfig;
diff --git a/apps/client/synthetic/next.config.mjs b/apps/client/synthetic/next.config.mjs
new file mode 100644
index 0000000..b4ba935
--- /dev/null
+++ b/apps/client/synthetic/next.config.mjs
@@ -0,0 +1,7 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+ transpilePackages: ["@gbv/config"],
+};
+
+export default nextConfig;
\ No newline at end of file
diff --git a/apps/client/synthetic/package.json b/apps/client/synthetic/package.json
new file mode 100644
index 0000000..36ff626
--- /dev/null
+++ b/apps/client/synthetic/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@gbv/synthetic-client",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "tsx scripts/dev.ts",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
+ "typecheck": "tsc -p tsconfig.json"
+ },
+ "dependencies": {
+ "@gbv/config": "workspace:*",
+ "next": "16.1.1",
+ "react": "19.2.3",
+ "react-dom": "19.2.3"
+ },
+ "devDependencies": {
+ "eslint": "^9.38.0",
+ "eslint-config-next": "16.1.1",
+ "@tailwindcss/postcss": "^4",
+ "tailwindcss": "^4"
+ }
+}
diff --git a/apps/client/synthetic/postcss.config.mjs b/apps/client/synthetic/postcss.config.mjs
new file mode 100644
index 0000000..61e3684
--- /dev/null
+++ b/apps/client/synthetic/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/apps/client/synthetic/public/favicon.ico b/apps/client/synthetic/public/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/apps/client/synthetic/public/favicon.ico differ
diff --git a/apps/client/synthetic/public/file.svg b/apps/client/synthetic/public/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/apps/client/synthetic/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/client/synthetic/public/globe.svg b/apps/client/synthetic/public/globe.svg
new file mode 100644
index 0000000..567f17b
--- /dev/null
+++ b/apps/client/synthetic/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/client/synthetic/public/next.svg b/apps/client/synthetic/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/apps/client/synthetic/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/client/synthetic/public/vercel.svg b/apps/client/synthetic/public/vercel.svg
new file mode 100644
index 0000000..7705396
--- /dev/null
+++ b/apps/client/synthetic/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/client/synthetic/public/window.svg b/apps/client/synthetic/public/window.svg
new file mode 100644
index 0000000..b2b2a44
--- /dev/null
+++ b/apps/client/synthetic/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/client/synthetic/scripts/dev.ts b/apps/client/synthetic/scripts/dev.ts
new file mode 100644
index 0000000..9e52156
--- /dev/null
+++ b/apps/client/synthetic/scripts/dev.ts
@@ -0,0 +1,18 @@
+#!/usr/bin/env node
+import { spawn } from "node:child_process";
+import { gbvConfig } from "@gbv/config";
+
+const child = spawn(
+ "corepack",
+ ["pnpm", "exec", "next", "dev", "-p", String(gbvConfig.ports.syntheticClient)],
+ {
+ stdio: "inherit",
+ shell: process.platform === "win32",
+ env: {
+ ...process.env,
+ PORT: String(gbvConfig.ports.syntheticClient),
+ },
+ },
+);
+
+child.on("exit", (code) => process.exit(code ?? 0));
diff --git a/apps/client/synthetic/src/lib/data.js b/apps/client/synthetic/src/lib/data.js
new file mode 100644
index 0000000..826932d
--- /dev/null
+++ b/apps/client/synthetic/src/lib/data.js
@@ -0,0 +1,231 @@
+const BASE_COURSES = [
+ {
+ courseId: 101,
+ publicCourseKey: "csk_7r2q9p",
+ slug: "introduction-microsoft-excel",
+ platformCourseId: "KDNGIJBDG",
+ certificateId: "O04CRAKZMLZJ",
+ title: "Getting Started with Microsoft Excel",
+ description:
+ "Learn the basics of Microsoft Excel, including formulas, functions, and data visualization.",
+ org: "Synthetic",
+ progressText: "100% complete",
+ gradeText: "Grade Achieved: 100%",
+ completedOn: "November 2 2025",
+ durationText: "1 hours approximately",
+ hasLinkedIn: true,
+ semanticProfile: {
+ moduleCountByPage: {
+ default: 5,
+ },
+ },
+ },
+ {
+ courseId: 202,
+ publicCourseKey: "csk_0a81lm",
+ slug: "data-structures-foundations",
+ platformCourseId: "PZ91KQ2RM",
+ certificateId: "744DP2JRWJDS",
+ title: "Data Structures Foundations",
+ description:
+ "Understand the fundamental concepts of data structures, including arrays, linked lists, and trees.",
+ org: "Synthetic",
+ progressText: "88% complete",
+ gradeText: "Grade Achieved: 96%",
+ completedOn: "October 14 2025",
+ durationText: "18 hours approximately",
+ hasLinkedIn: false,
+ semanticProfile: {
+ moduleCountByPage: {
+ default: 6,
+ assignments: 4,
+ },
+ },
+ },
+ {
+ courseId: 303,
+ publicCourseKey: "csk_3z19tt",
+ slug: "applied-ml-systems",
+ platformCourseId: "QW0N9X8AA",
+ certificateId: "C9T-81F2Z0G1",
+ title: "Applied ML Systems",
+ description: "Explore the application of machine learning techniques in real-world systems.",
+ org: "Synthetic",
+ progressText: "91% complete",
+ gradeText: "Grade Achieved: 94%",
+ completedOn: "September 8 2025",
+ durationText: "24 hours approximately",
+ hasLinkedIn: true,
+ semanticProfile: {
+ moduleCountByPage: {
+ default: 7,
+ },
+ certificateIdByPage: {
+ default: "C9T-81F2Z0G1",
+ proof: "C9T-81F2Z0G1-ALT",
+ },
+ },
+ },
+ {
+ courseId: 404,
+ publicCourseKey: "csk_t1mix",
+ slug: "secure-evidence-practicum",
+ platformCourseId: "MIXTIER1A",
+ certificateId: "MIX-7Q1Z-442K",
+ title: "Secure Evidence Practicum",
+ description:
+ "Practice artifact consistency analysis across provider surfaces and verification checkpoints.",
+ org: "Synthetic",
+ progressText: "100% complete",
+ gradeText: "Grade Achieved: 100%",
+ completedOn: "January 9 2026",
+ durationText: "12 hours approximately",
+ hasLinkedIn: false,
+ semanticProfile: {
+ moduleCountByPage: {
+ default: 6,
+ assignments: 4,
+ },
+ certificateIdByPage: {
+ default: "MIX-7Q1Z-442K",
+ proof: "MIX-7Q1Z-442K-PROOF",
+ },
+ gradePercentByPage: {
+ default: 100,
+ assignments: 50,
+ },
+ progressPercentByPage: {
+ default: 100,
+ assignments: 52,
+ },
+ courseKeyByPage: {
+ default: "csk_t1mix",
+ assignments: "csk_t1mix-shadow",
+ },
+ },
+ },
+];
+
+function parsePercent(text) {
+ const match = String(text || "").match(/(\d+(?:\.\d+)?)\s*%/);
+ return match ? Number.parseFloat(match[1]) : null;
+}
+
+function cloneCourse(course) {
+ return JSON.parse(JSON.stringify(course));
+}
+
+function formatPercentValue(value) {
+ if (!Number.isFinite(value)) return "";
+ return Number.isInteger(value) ? String(value) : String(Number(value).toFixed(1));
+}
+
+function formatGradeText(percent, fallback) {
+ const normalized = formatPercentValue(percent);
+ return normalized ? `Grade Achieved: ${normalized}%` : String(fallback || "");
+}
+
+function formatProgressText(percent, fallback) {
+ const normalized = formatPercentValue(percent);
+ return normalized ? `${normalized}% complete` : String(fallback || "");
+}
+
+function resolveByPage(course, pageType, key, fallback) {
+ const byPage = course?.semanticProfile?.[key] || {};
+ return byPage[pageType] ?? byPage.default ?? fallback;
+}
+
+function resolveGradePercent(course, pageType) {
+ const fallback = parsePercent(course?.gradeText);
+ const resolved = resolveByPage(course, pageType, "gradePercentByPage", fallback);
+ return Number.isFinite(resolved) ? Number(resolved) : fallback;
+}
+
+function resolveProgressPercent(course, pageType) {
+ const fallback = parsePercent(course?.progressText);
+ const resolved = resolveByPage(course, pageType, "progressPercentByPage", fallback);
+ return Number.isFinite(resolved) ? Number(resolved) : fallback;
+}
+
+function resolveModuleCount(course, pageType) {
+ const resolved = resolveByPage(course, pageType, "moduleCountByPage", null);
+ return Number.isFinite(resolved) ? Number(resolved) : null;
+}
+
+function resolveCertificateId(course, pageType) {
+ return String(
+ resolveByPage(course, pageType, "certificateIdByPage", course?.certificateId || ""),
+ );
+}
+
+function resolveCourseKey(course, pageType) {
+ return String(resolveByPage(course, pageType, "courseKeyByPage", course?.publicCourseKey || ""));
+}
+
+function resolveCourseName(course, pageType) {
+ return String(resolveByPage(course, pageType, "courseNameByPage", course?.title || ""));
+}
+
+function resolveCourseSlug(course, pageType) {
+ return String(resolveByPage(course, pageType, "courseSlugByPage", course?.slug || ""));
+}
+
+export function getSurfaceView(course, pageType) {
+ const gradePercent = resolveGradePercent(course, pageType);
+ const progressPercent = resolveProgressPercent(course, pageType);
+ const moduleCount = resolveModuleCount(course, pageType);
+ const certificateId = resolveCertificateId(course, pageType);
+ const courseKey = resolveCourseKey(course, pageType);
+ const courseName = resolveCourseName(course, pageType);
+ const courseSlug = resolveCourseSlug(course, pageType);
+
+ return {
+ gradePercent,
+ progressPercent,
+ moduleCount,
+ certificateId,
+ courseKey,
+ courseName,
+ courseSlug,
+ gradeText: formatGradeText(gradePercent, course?.gradeText),
+ progressText: formatProgressText(progressPercent, course?.progressText),
+ };
+}
+
+export function getCourses() {
+ return BASE_COURSES.map(cloneCourse);
+}
+
+export function getCourseByCert(certificateId) {
+ return getCourses().find((course) => course.certificateId === certificateId) || null;
+}
+
+export function getCourseByKey(publicCourseKey) {
+ return getCourses().find((course) => course.publicCourseKey === publicCourseKey) || null;
+}
+
+export function getCourseCatalog() {
+ return getCourses().map((course) => ({
+ courseId: course.courseId,
+ publicCourseKey: course.publicCourseKey,
+ certificateId: course.certificateId,
+ title: course.title,
+ slug: course.slug,
+ }));
+}
+
+export function getSemanticAttrs(course, pageType) {
+ const view = getSurfaceView(course, pageType);
+
+ return {
+ "data-gbv-semantic": "true",
+ "data-gbv-course-id": String(course?.courseId || ""),
+ "data-gbv-course-name": view.courseName,
+ "data-gbv-course-key": view.courseKey,
+ "data-gbv-course-slug": view.courseSlug,
+ "data-gbv-certificate-id": view.certificateId,
+ "data-gbv-grade-percent": view.gradePercent ?? "",
+ "data-gbv-progress-percent": view.progressPercent ?? "",
+ "data-gbv-module-count": view.moduleCount ?? "",
+ };
+}
diff --git a/apps/client/synthetic/src/pages/_app.js b/apps/client/synthetic/src/pages/_app.js
new file mode 100644
index 0000000..b97e52f
--- /dev/null
+++ b/apps/client/synthetic/src/pages/_app.js
@@ -0,0 +1,5 @@
+import "@/styles/globals.css";
+
+export default function App({ Component, pageProps }) {
+ return ;
+}
diff --git a/apps/client/synthetic/src/pages/_document.js b/apps/client/synthetic/src/pages/_document.js
new file mode 100644
index 0000000..628a733
--- /dev/null
+++ b/apps/client/synthetic/src/pages/_document.js
@@ -0,0 +1,13 @@
+import { Html, Head, Main, NextScript } from "next/document";
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/apps/client/synthetic/src/pages/api/gbv/courses.js b/apps/client/synthetic/src/pages/api/gbv/courses.js
new file mode 100644
index 0000000..2fb695b
--- /dev/null
+++ b/apps/client/synthetic/src/pages/api/gbv/courses.js
@@ -0,0 +1,8 @@
+import { getCourseCatalog } from "@/lib/data";
+
+export default function handler(_req, res) {
+ res.status(200).json({
+ ok: true,
+ courses: getCourseCatalog(),
+ });
+}
diff --git a/apps/client/synthetic/src/pages/c/[publicCourseKey]/assignments.js b/apps/client/synthetic/src/pages/c/[publicCourseKey]/assignments.js
new file mode 100644
index 0000000..9890020
--- /dev/null
+++ b/apps/client/synthetic/src/pages/c/[publicCourseKey]/assignments.js
@@ -0,0 +1,38 @@
+import AppShell from "@/ui/AppShell";
+import CourseHero from "@/ui/CourseHero";
+import AssignmentsTable from "@/ui/AssignmentsTable";
+import ActivityPanel from "@/ui/ActivityPanel";
+import { getCourseByKey, getSemanticAttrs, getSurfaceView } from "@/lib/data";
+
+export default function Assignments({ course }) {
+ if (!course) {
+ return (
+
+
+ Course not found.
+
+
+ );
+ }
+
+ const view = getSurfaceView(course, "assignments");
+
+ return (
+
+
+
+ );
+}
+
+export async function getServerSideProps(ctx) {
+ const course = getCourseByKey(ctx.params.publicCourseKey);
+ return { props: { course } };
+}
diff --git a/apps/client/synthetic/src/pages/c/[publicCourseKey]/index.js b/apps/client/synthetic/src/pages/c/[publicCourseKey]/index.js
new file mode 100644
index 0000000..43a2bea
--- /dev/null
+++ b/apps/client/synthetic/src/pages/c/[publicCourseKey]/index.js
@@ -0,0 +1,86 @@
+import AppShell from "@/ui/AppShell";
+import CourseHero from "@/ui/CourseHero";
+import ModulesList from "@/ui/ModulesList";
+import ActivityPanel from "@/ui/ActivityPanel";
+import { getCourseByKey, getSemanticAttrs, getSurfaceView } from "@/lib/data";
+
+export default function CourseHome({ course }) {
+ if (!course) {
+ return (
+
+
+ Course not found.
+
+
+ );
+ }
+
+ const view = getSurfaceView(course, "course");
+
+ return (
+
+
+
+
+
+
+
+
+
Overview
+
+ Learn through short lessons, hands-on labs, and graded
+ checkpoints. Your progress and scores update automatically.
+
+
+
+
+
Progress
+
+ {view.progressText}
+
+
+
+
Grade
+
+ {view.gradeText}
+
+
+
+
Completed
+
+ {course.completedOn}
+
+
+
+
+
+
+
+
+
+
+
+
Upcoming
+
+
+ Finish the next checkpoint quiz
+
+
+ Review feedback on labs
+
+
+ Export your completion record
+
+
+
+
+
+
+
+ );
+}
+
+export async function getServerSideProps(ctx) {
+ const course = getCourseByKey(ctx.params.publicCourseKey);
+ return { props: { course } };
+}
diff --git a/apps/client/synthetic/src/pages/c/[publicCourseKey]/module/[n].js b/apps/client/synthetic/src/pages/c/[publicCourseKey]/module/[n].js
new file mode 100644
index 0000000..919fcc9
--- /dev/null
+++ b/apps/client/synthetic/src/pages/c/[publicCourseKey]/module/[n].js
@@ -0,0 +1,62 @@
+import AppShell from "@/ui/AppShell";
+import CourseHero from "@/ui/CourseHero";
+import ModulesList from "@/ui/ModulesList";
+import { getCourseByKey, getSemanticAttrs } from "@/lib/data";
+
+export default function Module({ course, n }) {
+ if (!course) {
+ return (
+
+
+ Course not found.
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
Module {n}
+
Lesson flow
+
+ This page simulates a real module view with structured content and
+ consistent layout.
+
+
+
+
+
Recommended
+
+ Finish the checkpoint quiz
+
+
+ Estimated time: 8 min
+
+
+
+
Next up
+
+ Review lab feedback
+
+
+ Estimated time: 6 min
+
+
+
+
+
+
+
+
+ );
+}
+
+export async function getServerSideProps(ctx) {
+ const course = getCourseByKey(ctx.params.publicCourseKey);
+ return { props: { course, n: ctx.params.n } };
+}
diff --git a/apps/client/synthetic/src/pages/certificate/[certificateId].js b/apps/client/synthetic/src/pages/certificate/[certificateId].js
new file mode 100644
index 0000000..d1a9345
--- /dev/null
+++ b/apps/client/synthetic/src/pages/certificate/[certificateId].js
@@ -0,0 +1,139 @@
+import Link from "next/link";
+import AppShell from "@/ui/AppShell";
+import { getCourseByCert, getCourses, getSemanticAttrs, getSurfaceView } from "@/lib/data";
+
+function OtherCertificates({ currentCert, courses }) {
+ const others = courses.filter((course) => course.certificateId !== currentCert).slice(0, 3);
+
+ return (
+
+
+
+
Other certificates
+
Multiple artifacts are available.
+
+
+ All
+
+
+
+
+ {others.map((course) => (
+
+
{course.title}
+
{course.org}
+
+ ))}
+
+
+ );
+}
+
+export default function CertificatePage({ certificateId, course, courses }) {
+ if (!course) {
+ return (
+
+
+
Certificate not found
+
+ No certificate exists for ID {certificateId} .
+
+
+
+ Go to Milestones
+
+
+ Back to Hub
+
+
+
+
+ );
+ }
+
+ const view = getSurfaceView(course, "certificate");
+
+ return (
+
+
+
+
+
+
+
{course.org}
+
Certificate of Completion
+
+ This document certifies successful completion of the observed course.
+
+
+
+
+
+
Awarded for
+
{course.title}
+
+ Completion date: {course.completedOn}
+
+
+
+
+
Certificate ID
+
{view.certificateId}
+
+
+
Result
+
{view.gradeText}
+
+
+
+
+
+ Download
+
+
+ Print
+
+
+ Verify
+
+
+
+
+
+
+
+
+
Quick links
+
+
+ Verify record
+
+
+ Open course
+
+
+ Milestones
+
+
+ Hub
+
+
+
+
+
+
+
+
+ );
+}
+
+export async function getServerSideProps(ctx) {
+ const certificateId = ctx.params.certificateId;
+ const course = getCourseByCert(certificateId);
+ const courses = getCourses();
+ return { props: { certificateId, course, courses } };
+}
diff --git a/apps/client/synthetic/src/pages/hub.js b/apps/client/synthetic/src/pages/hub.js
new file mode 100644
index 0000000..d482ca4
--- /dev/null
+++ b/apps/client/synthetic/src/pages/hub.js
@@ -0,0 +1,71 @@
+import AppShell from "@/ui/AppShell";
+import CourseCard from "@/ui/CourseCard";
+import { getCourses } from "@/lib/data";
+
+export default function Hub({ courses }) {
+ return (
+
+
+
+
+
+
Welcome back
+
+ Pick up where you left off.
+
+
+
+
+ All
+ Completed
+ In progress
+
+
+ Explore
+
+
+
+
+
+
+
Courses
+
+ {courses.length}
+
+
+ In this workspace
+
+
+
+
Streak
+
+ 5 days
+
+
Keep it going
+
+
+
Next milestone
+
+ 2 modules
+
+
+ Until your next badge
+
+
+
+
+
+
+ {courses.map((c) => (
+
+ ))}
+
+
+
+ );
+}
+
+export async function getServerSideProps() {
+ const courses = getCourses();
+ return { props: { courses } };
+}
diff --git a/apps/client/synthetic/src/pages/index.js b/apps/client/synthetic/src/pages/index.js
new file mode 100644
index 0000000..d18dc3e
--- /dev/null
+++ b/apps/client/synthetic/src/pages/index.js
@@ -0,0 +1,23 @@
+import Link from "next/link";
+
+export default function Home() {
+ return (
+
+
+ Welcome to the Synthetic Learning Environment
+
+
+ This is a public demo UI for verification. Not all features are
+ available. Information is static.
+
+
+
+ Open
+
+
+
+ );
+}
diff --git a/apps/client/synthetic/src/pages/milestones.js b/apps/client/synthetic/src/pages/milestones.js
new file mode 100644
index 0000000..09b883f
--- /dev/null
+++ b/apps/client/synthetic/src/pages/milestones.js
@@ -0,0 +1,70 @@
+import Link from "next/link";
+import AppShell from "@/ui/AppShell";
+import { getCourses, getSurfaceView } from "@/lib/data";
+
+function RecordRow({ c }) {
+ const view = getSurfaceView(c, "milestones");
+
+ return (
+
+
+
+
+ {c.title}
+
+
{c.org}
+
+
+
+ {view.gradeText}
+
+
+
+
+
+
+ Create Certificate
+
+
+ View
+
+
+
+
+ );
+}
+
+export default function Milestones({ courses }) {
+ return (
+
+
+
+
Your records
+
+ All certificates and completions in one place.
+
+
+
+
+ {courses.map((c) => (
+
+ ))}
+
+
+
+ );
+}
+
+export async function getServerSideProps() {
+ const courses = getCourses();
+ return { props: { courses } };
+}
diff --git a/apps/client/synthetic/src/pages/proof/[certificateId].js b/apps/client/synthetic/src/pages/proof/[certificateId].js
new file mode 100644
index 0000000..7b5c4bb
--- /dev/null
+++ b/apps/client/synthetic/src/pages/proof/[certificateId].js
@@ -0,0 +1,140 @@
+import Link from "next/link";
+import AppShell from "@/ui/AppShell";
+import { getCourseByCert, getCourses, getSemanticAttrs, getSurfaceView } from "@/lib/data";
+
+function SimilarCourses({ currentCert, courses }) {
+ const others = courses.filter((course) => course.certificateId !== currentCert).slice(0, 3);
+
+ return (
+
+
+
+
Other records
+
Additional artifacts from the same platform.
+
+
+ View all
+
+
+
+
+ {others.map((course) => (
+
+
{course.title}
+
+ {course.org} | cert {course.certificateId}
+
+
+ ))}
+
+
+ );
+}
+
+export default function ProofPage({ certificateId, course, courses }) {
+ if (!course) {
+ return (
+
+
+
Record not found
+
+ We could not find a certificate with ID {certificateId} .
+
+
+
+ Go to Milestones
+
+
+ Back to Hub
+
+
+
+
+ );
+ }
+
+ const view = getSurfaceView(course, "proof");
+
+ return (
+
+
+
+
+
+
+
{course.org}
+
Verification
+
+ This surface confirms that the observed artifact data is coherent.
+
+
+
+
+
+
+
+
Course
+
{course.title}
+
+
+
Grade
+
{view.gradeText}
+
+
+
Completed
+
{course.completedOn}
+
+
+
Certificate ID
+
{view.certificateId}
+
+
+
+
+
+
+
+
Record details
+
+ This synthetic page intentionally mirrors what a real provider would expose.
+
+
+
+
+
Platform Course ID
+
{course.platformCourseId}
+
+
+
+
+ Open milestones
+
+
+ Open course
+
+
+ Certificate
+
+
+
+
+
+
+
+
+ );
+}
+
+export async function getServerSideProps(ctx) {
+ const certificateId = ctx.params.certificateId;
+ const course = getCourseByCert(certificateId);
+ const courses = getCourses();
+ return { props: { certificateId, course, courses } };
+}
diff --git a/apps/client/synthetic/src/styles/globals.css b/apps/client/synthetic/src/styles/globals.css
new file mode 100644
index 0000000..a2dc41e
--- /dev/null
+++ b/apps/client/synthetic/src/styles/globals.css
@@ -0,0 +1,26 @@
+@import "tailwindcss";
+
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ background: var(--background);
+ color: var(--foreground);
+ font-family: Arial, Helvetica, sans-serif;
+}
diff --git a/apps/client/synthetic/src/ui/ActivityPanel.js b/apps/client/synthetic/src/ui/ActivityPanel.js
new file mode 100644
index 0000000..898d615
--- /dev/null
+++ b/apps/client/synthetic/src/ui/ActivityPanel.js
@@ -0,0 +1,54 @@
+export default function ActivityPanel({ course }) {
+ const items = [
+ [
+ "Checkpoint completed",
+ "Graded checkpoint recorded",
+ "2 hours ago",
+ "blue",
+ ],
+ ["Practice set", "New practice questions available", "yesterday", "sky"],
+ ["Module progress", "Lesson items updated", "2 days ago", "indigo"],
+ [
+ "Sharing",
+ course.hasLinkedIn ? "Sharing enabled" : "Sharing unavailable",
+ "3 days ago",
+ "slate",
+ ],
+ ];
+
+ const dot = (tone) =>
+ tone === "blue"
+ ? "bg-blue-600"
+ : tone === "sky"
+ ? "bg-sky-500"
+ : tone === "indigo"
+ ? "bg-indigo-500"
+ : "bg-slate-400";
+
+ return (
+
+
Activity
+
Updates and reminders
+
+
+ {items.map(([title, desc, when, tone]) => (
+
+ ))}
+
+
+
+ Tip: finishing one module a day builds a strong streak.
+
+
+ );
+}
diff --git a/apps/client/synthetic/src/ui/AppShell.js b/apps/client/synthetic/src/ui/AppShell.js
new file mode 100644
index 0000000..dbaf0ed
--- /dev/null
+++ b/apps/client/synthetic/src/ui/AppShell.js
@@ -0,0 +1,104 @@
+import Link from "next/link";
+import { useRouter } from "next/router";
+
+function NavLink({ href, label }) {
+ const r = useRouter();
+ const active = r.pathname === href;
+ return (
+
+ {label}
+ ›
+
+ );
+}
+
+export default function AppShell({ children, title, subtitle, right }) {
+ const r = useRouter();
+ return (
+
+ {/* Top Bar */}
+
+
+
{
+ r.push("/hub");
+ }}
+ className="h-9 w-9 cursor-pointer rounded-xl bg-gradient-to-br from-blue-500 to-sky-400 shadow-sm"
+ />
+
+
+ {title || "LearnSpace"}
+
+
+ {subtitle || "Your courses, progress, and certificates"}
+
+
+
+
+
+ Search
+
+
+ /
+
+
+
+ {right || (
+
+ New
+
+ )}
+
+
+
+
+ {/* Body */}
+
+ {/* Sidebar */}
+
+
+
+
+ Workspace
+
+
+
+
+
+
+
+
+
Today
+
+
+
Quick tips
+
Keep learning streaks consistent.
+
Finish one module at a time.
+
+
+
Updates
+
New practice sets available.
+
Certificates now support sharing.
+
+
+
+
+
+
+ {/* Main */}
+
{children}
+
+
+ );
+}
diff --git a/apps/client/synthetic/src/ui/AssignmentsTable.js b/apps/client/synthetic/src/ui/AssignmentsTable.js
new file mode 100644
index 0000000..1e65a11
--- /dev/null
+++ b/apps/client/synthetic/src/ui/AssignmentsTable.js
@@ -0,0 +1,64 @@
+function assignmentRows() {
+ return [
+ ["Spreadsheet Basics", "Quiz", "10 questions", "100%"],
+ ["Functions and Formulas", "Lab", "auto-graded", "96%"],
+ ["Charts and Pivot Tables", "Lab", "peer-reviewed", "92%"],
+ ["Final Checkpoint", "Quiz", "timed", "100%"],
+ ["Optimization Drill", "Lab", "auto-graded", "95%"],
+ ["Portfolio Reflection", "Discussion", "ungraded", "N/A"],
+ ];
+}
+
+export default function AssignmentsTable({ course, view = null }) {
+ const rows = assignmentRows();
+ const configuredCount = Number(
+ course?.semanticProfile?.moduleCountByPage?.assignments ??
+ course?.semanticProfile?.moduleCountByPage?.default ??
+ 4,
+ );
+ const rowCount = Math.max(1, Math.min(rows.length, configuredCount));
+ const visibleRows = rows.slice(0, rowCount);
+
+ return (
+
+
+
+
Assignments
+
+ Deadlines, scores, and feedback
+
+
+
+ {view?.gradeText || course.gradeText}
+
+
+
+
+
+
+
+ Item
+ Type
+ Rules
+ Score
+
+
+
+ {visibleRows.map((row) => (
+
+ {row[0]}
+ {row[1]}
+ {row[2]}
+ {row[3]}
+
+ ))}
+
+
+
+
+
+ Visible rows: {visibleRows.length}
+
+
+ );
+}
diff --git a/apps/client/synthetic/src/ui/CourseCard.js b/apps/client/synthetic/src/ui/CourseCard.js
new file mode 100644
index 0000000..149ec22
--- /dev/null
+++ b/apps/client/synthetic/src/ui/CourseCard.js
@@ -0,0 +1,104 @@
+import Link from "next/link";
+import { getSurfaceView } from "@/lib/data";
+
+function Badge({ children, tone = "blue" }) {
+ const cls =
+ tone === "blue"
+ ? "bg-blue-50 text-blue-700 border border-slate-200/40-blue-100"
+ : tone === "green"
+ ? "bg-emerald-50 text-emerald-700 border border-slate-200/40-emerald-100"
+ : "bg-slate-50 text-slate-700 border border-slate-200/40-slate-200";
+ return (
+
+ {children}
+
+ );
+}
+
+function ProgressBar({ percent }) {
+ return (
+
+ );
+}
+
+export default function CourseCard({ c }) {
+ const view = getSurfaceView(c, "hub");
+ const pct = Math.max(0, Math.min(100, parseInt(view.progressText) || 0));
+ const completed = pct >= 100;
+
+ return (
+
+
+
+
{c.org}
+
+ {c.title}
+
+
+
+ {completed ? (
+ Completed
+ ) : (
+ In progress
+ )}
+
+
+
+
+ {view.progressText}
+ {pct}%
+
+
+
+
+
+
+
+ Verify
+
+
+ Assignments
+
+
+ Open
+
+
+
+
+
+
+
Grade
+
{view.gradeText}
+
+
+
Completed
+
{c.completedOn}
+
+
+
Duration
+
{c.durationText}
+
+
+
+ );
+}
diff --git a/apps/client/synthetic/src/ui/CourseHero.js b/apps/client/synthetic/src/ui/CourseHero.js
new file mode 100644
index 0000000..9fe71b5
--- /dev/null
+++ b/apps/client/synthetic/src/ui/CourseHero.js
@@ -0,0 +1,89 @@
+import Link from "next/link";
+
+function Pill({ children }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export default function CourseHero({ course, view = null, active = "overview" }) {
+ const certificateId = view?.certificateId || course.certificateId;
+ const progressText = view?.progressText || course.progressText;
+ const gradeText = view?.gradeText || course.gradeText;
+
+ return (
+
+
+
+
+
{course.org}
+
{course.title}
+
+
+
+
+
+ Certificate
+
+
+ Verify
+
+
+
+
+
+
+
+
+ {[
+ ["overview", "Overview", `/c/${course.publicCourseKey}`],
+ ["modules", "Modules", null],
+ [
+ "assignments",
+ "Assignments",
+ `/c/${course.publicCourseKey}/assignments`,
+ ],
+ ["grades", "Grades", null],
+ ["discussion", "Discussion", null],
+ ].map(([k, label, url]) => (
+
url && (window.location.href = url)}
+ className={[
+ "rounded-xl px-3 py-2 text-sm border border-slate-200/40",
+ active === k
+ ? "border border-slate-200/40-blue-600 bg-blue-600 text-white"
+ : !url
+ ? "bg-black/5 text-black/10 cursor-not-allowed"
+ : "cursor-pointer border border-slate-200/40-slate-200 bg-white text-slate-700 hover:bg-slate-50",
+ ].join(" ")}
+ >
+ {label}
+
+ ))}
+
+
+
+ Progress: {progressText} | {gradeText} | Cert {certificateId}
+
+
+
+
+ );
+}
+
diff --git a/apps/client/synthetic/src/ui/ModulesList.js b/apps/client/synthetic/src/ui/ModulesList.js
new file mode 100644
index 0000000..00ad1dc
--- /dev/null
+++ b/apps/client/synthetic/src/ui/ModulesList.js
@@ -0,0 +1,60 @@
+function Row({ idx, title, meta, done }) {
+ return (
+
+
+ {idx}
+
+
+
{done ? "Done" : "Next"}
+
+ );
+}
+
+export default function ModulesList({ course }) {
+ const moduleTemplates = [
+ ["Welcome and Setup", "Video | 6 min | checklist"],
+ ["Core Concepts", "Reading | 12 min | notes"],
+ ["Hands-on Lab", "Interactive | 25 min | graded"],
+ ["Checkpoint Quiz", "Quiz | 10 questions | graded"],
+ ["Wrap-up and Export", "Summary | share | proof"],
+ ["Data Hygiene", "Lab | 15 min | graded"],
+ ["Capstone Build", "Project | 30 min | graded"],
+ ["Final Reflection", "Discussion | 10 min | ungraded"],
+ ];
+
+ const configuredCount = Number(course?.semanticProfile?.moduleCountByPage?.default || 5);
+ const moduleCount = Math.max(1, Math.min(moduleTemplates.length, configuredCount));
+ const modules = moduleTemplates.slice(0, moduleCount);
+
+ const seed = course.platformCourseId.charCodeAt(0) % modules.length;
+
+ return (
+
+
+
+
Modules
+
+ Structured lessons with realistic rows
+
+
+
+ Count: {moduleCount}
+
+
+
+
+ {modules.map(([title, meta], index) => (
+
+ ))}
+
+
+ );
+}
diff --git a/apps/client/synthetic/tsconfig.json b/apps/client/synthetic/tsconfig.json
new file mode 100644
index 0000000..7f5f8c4
--- /dev/null
+++ b/apps/client/synthetic/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": false,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "src/**/*", "scripts/**/*.ts"],
+ "exclude": ["node_modules"]
+}
\ No newline at end of file
diff --git a/apps/demo-web/README.md b/apps/demo-web/README.md
deleted file mode 100644
index 86d84cd..0000000
--- a/apps/demo-web/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Demo Web
diff --git a/apps/extension/README.md b/apps/extension/README.md
new file mode 100644
index 0000000..b2100f6
--- /dev/null
+++ b/apps/extension/README.md
@@ -0,0 +1,31 @@
+# @gbv/extension
+
+MV3 GBV browser client.
+
+## Components
+
+- popup UI (`src/popup/*`)
+- background service worker (`src/background.ts`)
+- content script (`src/contentScript.ts`)
+- flow orchestration (`src/lib/gbvFlow.ts`)
+
+## Build
+
+From repo root:
+
+- `corepack pnpm build:extension`
+
+Build output:
+- `apps/extension/build`
+
+## Load in Chrome
+
+1. Open `chrome://extensions`
+2. Enable Developer Mode
+3. Load unpacked -> `apps/extension/build`
+
+## Runtime Flow
+
+popup -> background -> content script -> page context -> background -> server
+
+All host permissions and protocol constants come from `gbv.config.ts`.
diff --git a/apps/extension/eslint.config.mjs b/apps/extension/eslint.config.mjs
new file mode 100644
index 0000000..428f3c6
--- /dev/null
+++ b/apps/extension/eslint.config.mjs
@@ -0,0 +1,27 @@
+import js from "@eslint/js";
+import tseslint from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+import globals from "globals";
+import { defineConfig } from "eslint/config";
+
+export default defineConfig([
+ js.configs.recommended,
+ {
+ files: ["src/**/*.{ts,tsx}"],
+ languageOptions: {
+ parser: tsParser,
+ ecmaVersion: 2022,
+ sourceType: "module",
+ globals: {
+ ...globals.browser,
+ ...globals.webextensions,
+ },
+ },
+ plugins: {
+ "@typescript-eslint": tseslint,
+ },
+ rules: {
+ ...tseslint.configs.recommended.rules,
+ },
+ },
+]);
diff --git a/apps/extension/index.html b/apps/extension/index.html
new file mode 100644
index 0000000..a6de6f2
--- /dev/null
+++ b/apps/extension/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
GBV Client
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/extension/package.json b/apps/extension/package.json
new file mode 100644
index 0000000..76118dd
--- /dev/null
+++ b/apps/extension/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "@gbv/extension",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "prebuild": "tsx ../../scripts/generate-extension-manifest.ts",
+ "build": "vite build --config vite.popup.config.ts && vite build --config vite.background.config.ts && vite build --config vite.content.config.ts",
+ "dev": "vite build --config vite.popup.config.ts --watch",
+ "lint": "eslint src --ext .ts,.tsx",
+ "typecheck": "tsc -p tsconfig.json",
+ "test": "tsx --test src/**/*.test.ts"
+ },
+ "dependencies": {
+ "@gbv/config": "workspace:*",
+ "@gbv/core": "workspace:*",
+ "react": "19.2.3",
+ "react-dom": "19.2.3"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.19.0",
+ "@typescript-eslint/eslint-plugin": "^8.46.2",
+ "@typescript-eslint/parser": "^8.46.2",
+ "@types/chrome": "^0.0.315",
+ "@types/react": "^19.0.8",
+ "@types/react-dom": "^19.0.3",
+ "@vitejs/plugin-react-swc": "^3.8.0",
+ "eslint": "^9.19.0",
+ "globals": "^15.14.0",
+ "tsx": "^4.21.0",
+ "typescript": "^5.7.3",
+ "vite": "^6.0.11"
+ }
+}
diff --git a/apps/extension/public/manifest.template.json b/apps/extension/public/manifest.template.json
new file mode 100644
index 0000000..1d1b25f
--- /dev/null
+++ b/apps/extension/public/manifest.template.json
@@ -0,0 +1,14 @@
+{
+ "manifest_version": 3,
+ "name": "__GBV_EXTENSION_NAME__",
+ "version": "__GBV_EXTENSION_VERSION__",
+ "action": {
+ "default_title": "GBV Client",
+ "default_popup": "index.html"
+ },
+ "background": {
+ "service_worker": "background.js"
+ },
+ "permissions": __GBV_PERMISSIONS__,
+ "host_permissions": __GBV_HOST_PERMISSIONS__
+}
diff --git a/apps/extension/src/background.ts b/apps/extension/src/background.ts
new file mode 100644
index 0000000..cce995b
--- /dev/null
+++ b/apps/extension/src/background.ts
@@ -0,0 +1,45 @@
+import { runGbvFlow } from "./lib/gbvFlow";
+import { debugLog } from "./lib/log";
+import type { PingMessage, PingResponse, RunFlowMessage, RunFlowResponse } from "./lib/messages";
+
+/**
+ * Background message router for GBV extension actions.
+ */
+chrome.runtime.onMessage.addListener(
+ (
+ message: RunFlowMessage | PingMessage,
+ _sender,
+ sendResponse: (response: RunFlowResponse | PingResponse) => void,
+ ) => {
+ if (message.type === "GBV_PING") {
+ sendResponse({
+ ok: true,
+ serviceWorker: "ready",
+ ts: new Date().toISOString(),
+ });
+ return false;
+ }
+
+ if (message.type !== "GBV_RUN_FLOW") {
+ return false;
+ }
+
+ debugLog("[GBV][bg] popup requested run", { artifact: message.artifact });
+ runGbvFlow(message.artifact)
+ .then((result) => {
+ debugLog("[GBV][bg] flow completed", { status: result.status });
+ sendResponse({ ok: true, result });
+ })
+ .catch((error) =>
+ (debugLog("[GBV][bg] flow failed", {
+ error: String((error as Error)?.message || error),
+ }),
+ sendResponse({
+ ok: false,
+ error: String((error as Error)?.message || error),
+ })),
+ );
+
+ return true;
+ },
+);
diff --git a/apps/extension/src/contentScript.ts b/apps/extension/src/contentScript.ts
new file mode 100644
index 0000000..afedd6b
--- /dev/null
+++ b/apps/extension/src/contentScript.ts
@@ -0,0 +1,252 @@
+import { gbvConfig } from "@gbv/config";
+import { syntheticProvider } from "@gbv/core/browser";
+import { debugLog } from "./lib/log";
+import type {
+ CollectSurfaceMessage,
+ CollectSurfaceResponse,
+ PageCollectRequest,
+ PageCollectResponse,
+ PageSnapshot,
+} from "./lib/messages";
+
+const PAGE_BRIDGE_SCRIPT_ID = "__GBV_PAGE_BRIDGE__";
+const PAGE_COLLECT_REQUEST_TYPE = "GBV_PAGE_COLLECT_REQUEST";
+const PAGE_COLLECT_RESPONSE_TYPE = "GBV_PAGE_COLLECT_RESPONSE";
+const PAGE_BRIDGE_TIMEOUT_MS = 350;
+
+function pageBridgeMain() {
+ const requestType = "GBV_PAGE_COLLECT_REQUEST";
+ const responseType = "GBV_PAGE_COLLECT_RESPONSE";
+ const guardKey = "__GBV_PAGE_BRIDGE_READY__";
+ const globalWindow = window as typeof window & { [key: string]: unknown };
+
+ if (globalWindow[guardKey]) return;
+ globalWindow[guardKey] = true;
+
+ window.addEventListener("message", (event: MessageEvent) => {
+ if (event.source !== window) return;
+
+ const data = event.data as { type?: string; requestId?: string; nonce?: string; nonceLeafId?: string };
+ if (!data || data.type !== requestType) return;
+
+ const requestId = String(data.requestId || "");
+ const nonce = String(data.nonce || "");
+ const nonceLeafId = String(data.nonceLeafId || "");
+
+ try {
+ if (!requestId) {
+ throw new Error("Missing requestId");
+ }
+ if (!nonceLeafId) {
+ throw new Error("Missing nonceLeafId");
+ }
+
+ const selector = `meta[${nonceLeafId}]`;
+ let nonceNode = document.querySelector(selector) as HTMLMetaElement | null;
+ if (!nonceNode) {
+ nonceNode = document.createElement("meta");
+ nonceNode.setAttribute(nonceLeafId, nonce);
+ (document.head || document.documentElement).appendChild(nonceNode);
+ } else {
+ nonceNode.setAttribute(nonceLeafId, nonce);
+ }
+
+ const snapshot = {
+ url: window.location.href,
+ html: document.documentElement?.outerHTML || "",
+ injectedNonce: nonceNode.getAttribute(nonceLeafId) || "",
+ };
+
+ window.postMessage(
+ {
+ type: responseType,
+ requestId,
+ ok: true,
+ snapshot,
+ },
+ "*",
+ );
+ } catch (error) {
+ window.postMessage(
+ {
+ type: responseType,
+ requestId,
+ ok: false,
+ error: String((error as Error)?.message || error),
+ },
+ "*",
+ );
+ }
+ });
+}
+
+/**
+ * Inject a tiny page-context bridge so evidence capture happens in the page world.
+ *
+ * Spec Stage II.
+ */
+function installPageBridge(): void {
+ if (document.getElementById(PAGE_BRIDGE_SCRIPT_ID)) return;
+
+ const script = document.createElement("script");
+ script.id = PAGE_BRIDGE_SCRIPT_ID;
+ script.textContent = `;(${pageBridgeMain.toString()})();`;
+ (document.head || document.documentElement).appendChild(script);
+ script.remove();
+ debugLog("[GBV][cs] page bridge installed", { url: window.location.href });
+}
+
+function requestSnapshotFromPage(message: CollectSurfaceMessage): Promise
{
+ installPageBridge();
+
+ return new Promise((resolve, reject) => {
+ const timeout = setTimeout(() => {
+ window.removeEventListener("message", onMessage);
+ reject(new Error("Timed out waiting for page snapshot response"));
+ }, PAGE_BRIDGE_TIMEOUT_MS);
+
+ const onMessage = (event: MessageEvent) => {
+ if (event.source !== window) return;
+
+ const response = event.data as PageCollectResponse;
+ if (!response || response.type !== PAGE_COLLECT_RESPONSE_TYPE) return;
+ if (response.requestId !== message.requestId) return;
+
+ clearTimeout(timeout);
+ window.removeEventListener("message", onMessage);
+
+ if (!response.ok) {
+ reject(new Error(response.error || "Page snapshot collection failed"));
+ return;
+ }
+
+ resolve(response.snapshot);
+ };
+
+ window.addEventListener("message", onMessage);
+
+ const request: PageCollectRequest = {
+ type: PAGE_COLLECT_REQUEST_TYPE,
+ requestId: message.requestId,
+ nonce: message.nonce,
+ nonceLeafId: message.nonceLeafId,
+ };
+ window.postMessage(request, "*");
+ });
+}
+
+function collectSnapshotDirectly(message: CollectSurfaceMessage): PageSnapshot {
+ const selector = `meta[${message.nonceLeafId}]`;
+ let nonceNode = document.querySelector(selector) as HTMLMetaElement | null;
+
+ if (!nonceNode) {
+ nonceNode = document.createElement("meta");
+ nonceNode.setAttribute(message.nonceLeafId, message.nonce);
+ (document.head || document.documentElement).appendChild(nonceNode);
+ } else {
+ nonceNode.setAttribute(message.nonceLeafId, message.nonce);
+ }
+
+ return {
+ url: window.location.href,
+ html: document.documentElement?.outerHTML || "",
+ injectedNonce: nonceNode.getAttribute(message.nonceLeafId) || "",
+ };
+}
+
+/**
+ * Capture and canonicalize current surface evidence.
+ *
+ * Spec Stage II-IV.
+ */
+async function collectSurface(message: CollectSurfaceMessage): Promise {
+ try {
+ debugLog("[GBV][cs] collectSurface request", {
+ requestId: message.requestId,
+ url: window.location.href,
+ nonceLeafId: message.nonceLeafId,
+ });
+ const currentOrigin = new URL(window.location.href).origin;
+ if (currentOrigin !== gbvConfig.origins.syntheticClient) {
+ throw new Error(`Unexpected origin ${currentOrigin}; expected ${gbvConfig.origins.syntheticClient}`);
+ }
+
+ let snapshot: PageSnapshot;
+ try {
+ snapshot = await requestSnapshotFromPage(message);
+ debugLog("[GBV][cs] page-bridge snapshot collected", {
+ requestId: message.requestId,
+ });
+ } catch (bridgeError) {
+ debugLog("[GBV][cs] page-bridge failed, using direct snapshot fallback", {
+ requestId: message.requestId,
+ error: String((bridgeError as Error)?.message || bridgeError),
+ });
+ snapshot = collectSnapshotDirectly(message);
+ }
+ if (!snapshot.html || !snapshot.url) {
+ throw new Error("Collected snapshot is missing html/url");
+ }
+ if (snapshot.html.length > gbvConfig.protocol.maxHtmlChars) {
+ throw new Error("Collected snapshot exceeds max html size limit");
+ }
+ if (!snapshot.injectedNonce) {
+ throw new Error("Injected nonce is missing from collected page");
+ }
+
+ const canonicalLeaves = syntheticProvider.canonicalize({
+ html: snapshot.html,
+ url: snapshot.url,
+ nonceLeafId: message.nonceLeafId,
+ });
+
+ return {
+ ok: true,
+ surface: {
+ url: snapshot.url,
+ html: snapshot.html,
+ injectedNonce: snapshot.injectedNonce,
+ pageType: syntheticProvider.detectPageType(snapshot.url),
+ canonicalLeaves,
+ nonceMetadata: {
+ id: message.nonceLeafId,
+ value: snapshot.injectedNonce,
+ },
+ observedAt: new Date().toISOString(),
+ },
+ };
+ } catch (error) {
+ debugLog("[GBV][cs] collectSurface error", {
+ requestId: message.requestId,
+ error: String((error as Error)?.message || error),
+ url: window.location.href,
+ });
+ return {
+ ok: false,
+ error: String((error as Error)?.message || error),
+ };
+ }
+}
+
+chrome.runtime.onMessage.addListener(
+ (
+ message: CollectSurfaceMessage,
+ _sender,
+ sendResponse: (response: CollectSurfaceResponse) => void,
+ ) => {
+ if (message.type !== "GBV_COLLECT_SURFACE") {
+ return false;
+ }
+
+ debugLog("[GBV][cs] runtime message received", {
+ requestId: message.requestId,
+ type: message.type,
+ url: window.location.href,
+ });
+ collectSurface(message).then(sendResponse);
+ return true;
+ },
+);
+
+debugLog("[GBV][cs] content script loaded", { url: window.location.href });
+installPageBridge();
diff --git a/apps/extension/src/lib/gbvFlow.ts b/apps/extension/src/lib/gbvFlow.ts
new file mode 100644
index 0000000..9cb39bc
--- /dev/null
+++ b/apps/extension/src/lib/gbvFlow.ts
@@ -0,0 +1,262 @@
+import { gbvConfig } from "@gbv/config";
+import {
+ buildSurfaceUrlsFromPlan,
+ type GbvPageType,
+ type GbvVerificationArtifact,
+} from "@gbv/core/browser";
+import type {
+ CanonicalizedSurface,
+ CollectSurfaceMessage,
+ CollectSurfaceResponse,
+ GbvSubmitResponsePayload,
+ RunFlowResult,
+} from "./messages";
+import { debugLog } from "./log";
+
+type InitResponse = {
+ ok: boolean;
+ sessionId: string;
+ challengeNonce: string;
+ artifact: GbvVerificationArtifact;
+ pagePlan: string[];
+ pageNonces: string[];
+ nonceLeafId: string;
+};
+
+/**
+ * Wait for a tab to complete loading before sending collection commands.
+ *
+ * Spec Stage II.
+ */
+async function waitForTabReady(tabId: number, timeoutMs = 20_000): Promise {
+ const startedAt = Date.now();
+
+ while (Date.now() - startedAt < timeoutMs) {
+ const tab = await chrome.tabs.get(tabId);
+ if (tab.status === "complete") return;
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ }
+
+ throw new Error(`Timed out waiting for tab ${tabId} to finish loading`);
+}
+
+async function openObservationTab(url: string): Promise {
+ debugLog("[GBV][bg] opening observation tab", { url });
+ const tab = await chrome.tabs.create({ url, active: false });
+ if (!tab.id) {
+ throw new Error(`Failed to create observation tab for ${url}`);
+ }
+ debugLog("[GBV][bg] observation tab opened", { tabId: tab.id, url: tab.url });
+ return tab.id;
+}
+
+async function closeTab(tabId: number): Promise {
+ try {
+ debugLog("[GBV][bg] closing tab", { tabId });
+ await chrome.tabs.remove(tabId);
+ } catch {
+ // ignored during cleanup when tabs are already closed
+ }
+}
+
+/**
+ * Ensure the content script is present in the tab before message-based collection.
+ */
+async function ensureContentScriptInjected(tabId: number): Promise {
+ try {
+ debugLog("[GBV][bg] injecting content script", { tabId });
+ await chrome.scripting.executeScript({
+ target: { tabId },
+ files: ["contentScript.js"],
+ });
+ debugLog("[GBV][bg] content script injected", { tabId });
+ } catch (error) {
+ const message = String((error as Error)?.message || error);
+ debugLog("[GBV][bg] content script injection error", { tabId, message });
+ // Ignore duplicate-injection failures and continue with message path.
+ if (
+ message.includes("Cannot access contents of the page") ||
+ message.includes("The extensions gallery cannot be scripted")
+ ) {
+ throw new Error(`Cannot inject GBV content script into tab ${tabId}: ${message}`);
+ }
+ }
+}
+
+function sendCollectMessage(
+ tabId: number,
+ message: CollectSurfaceMessage,
+): Promise {
+ debugLog("[GBV][bg] sending collect message", {
+ tabId,
+ requestId: message.requestId,
+ nonceLeafId: message.nonceLeafId,
+ });
+ return new Promise((resolve, reject) => {
+ chrome.tabs.sendMessage(tabId, message, (response: CollectSurfaceResponse | undefined) => {
+ if (chrome.runtime.lastError) {
+ debugLog("[GBV][bg] sendMessage lastError", {
+ tabId,
+ error: chrome.runtime.lastError.message,
+ });
+ reject(new Error(chrome.runtime.lastError.message));
+ return;
+ }
+
+ if (!response?.ok) {
+ debugLog("[GBV][bg] collect response failed", { tabId, response });
+ reject(new Error(response?.error || "Surface collection failed"));
+ return;
+ }
+
+ debugLog("[GBV][bg] collect response success", {
+ tabId,
+ url: response.surface.url,
+ pageType: response.surface.pageType,
+ });
+ resolve(response.surface);
+ });
+ });
+}
+
+/**
+ * Collect canonicalized evidence from content script with retries while the tab bootstraps.
+ *
+ * Spec Stage II-IV.
+ */
+async function collectSurfaceFromTab(
+ tabId: number,
+ nonce: string,
+ nonceLeafId: string,
+): Promise {
+ const maxAttempts = 30;
+ const requestId = crypto.randomUUID();
+ let lastError: unknown = null;
+
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
+ try {
+ debugLog("[GBV][bg] collect attempt", { tabId, attempt, maxAttempts });
+ if (attempt === 1) {
+ await ensureContentScriptInjected(tabId);
+ }
+ return await sendCollectMessage(tabId, {
+ type: "GBV_COLLECT_SURFACE",
+ requestId,
+ nonce,
+ nonceLeafId,
+ });
+ } catch (error) {
+ lastError = error;
+ const message = String((error as Error)?.message || error);
+ const likelyBootRace =
+ message.includes("Could not establish connection") ||
+ message.includes("Receiving end does not exist") ||
+ message.includes("The message port closed");
+
+ debugLog("[GBV][bg] collect attempt failed", {
+ tabId,
+ attempt,
+ message,
+ likelyBootRace,
+ });
+
+ if (!likelyBootRace || attempt === maxAttempts) {
+ break;
+ }
+
+ await ensureContentScriptInjected(tabId);
+ await new Promise((resolve) => setTimeout(resolve, 150));
+ }
+ }
+
+ throw new Error(
+ `Unable to collect surface from tab ${tabId}: ${String(lastError || "unknown")}`,
+ );
+}
+
+/**
+ * Execute full GBV browser flow from background service worker.
+ *
+ * Spec Stage I-V: challenge issuance, multi-surface observation,
+ * canonicalization, and submission.
+ */
+export async function runGbvFlow(artifact: GbvVerificationArtifact): Promise {
+ const startedAtMs = Date.now();
+ const startedAt = new Date(startedAtMs).toISOString();
+ debugLog("[GBV][bg] starting GBV flow", { artifact });
+ const initResponse = await fetch(`${gbvConfig.origins.server}/api/gbv/init`, {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify({ artifact }),
+ });
+
+ const initJson = (await initResponse.json()) as InitResponse;
+ if (!initResponse.ok || !initJson.ok) {
+ throw new Error(`GBV init failed: ${JSON.stringify(initJson)}`);
+ }
+ debugLog("[GBV][bg] init success", {
+ sessionId: initJson.sessionId,
+ pagePlan: initJson.pagePlan,
+ });
+
+ const surfaceUrls = buildSurfaceUrlsFromPlan({
+ origin: gbvConfig.origins.syntheticClient,
+ pagePlan: initJson.pagePlan as GbvPageType[],
+ templates: gbvConfig.demo.surfacePathTemplates,
+ artifact,
+ });
+
+ const observations: CanonicalizedSurface[] = [];
+
+ for (let index = 0; index < surfaceUrls.length; index += 1) {
+ const url = surfaceUrls[index];
+ const nonce = initJson.pageNonces[index];
+ const tabId = await openObservationTab(url);
+
+ try {
+ await waitForTabReady(tabId);
+ const surface = await collectSurfaceFromTab(tabId, nonce, initJson.nonceLeafId);
+ observations.push(surface);
+ } finally {
+ await closeTab(tabId);
+ }
+ }
+
+ const pages = observations.map((surface) => ({
+ url: surface.url,
+ html: surface.html,
+ injectedNonce: surface.injectedNonce,
+ }));
+
+ const submitResponse = await fetch(`${gbvConfig.origins.server}/api/gbv/submit`, {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify({
+ sessionId: initJson.sessionId,
+ courseId: artifact.courseId,
+ provider: gbvConfig.protocol.providerId,
+ pages,
+ }),
+ });
+
+ const submitBody = (await submitResponse.json()) as GbvSubmitResponsePayload;
+ debugLog("[GBV][bg] submit completed", {
+ status: submitResponse.status,
+ observationCount: observations.length,
+ });
+ const canonicalLeafCount = new Set(observations.flatMap((surface) => surface.canonicalLeaves))
+ .size;
+ const finishedAtMs = Date.now();
+
+ return {
+ status: submitResponse.status,
+ body: submitBody,
+ artifact,
+ observationCount: observations.length,
+ canonicalLeafCount,
+ sessionId: initJson.sessionId,
+ startedAt,
+ finishedAt: new Date(finishedAtMs).toISOString(),
+ durationMs: finishedAtMs - startedAtMs,
+ };
+}
diff --git a/apps/extension/src/lib/log.ts b/apps/extension/src/lib/log.ts
new file mode 100644
index 0000000..75f5615
--- /dev/null
+++ b/apps/extension/src/lib/log.ts
@@ -0,0 +1,14 @@
+import { gbvConfig } from "@gbv/config";
+
+function enabled(): boolean {
+ return Boolean(gbvConfig.environment.debug);
+}
+
+export function debugLog(message: string, payload?: unknown): void {
+ if (!enabled()) return;
+ if (payload === undefined) {
+ console.debug(message);
+ return;
+ }
+ console.debug(message, payload);
+}
diff --git a/apps/extension/src/lib/messages.ts b/apps/extension/src/lib/messages.ts
new file mode 100644
index 0000000..f81a357
--- /dev/null
+++ b/apps/extension/src/lib/messages.ts
@@ -0,0 +1,183 @@
+import type { GbvPageType, GbvVerificationArtifact } from "@gbv/core/browser";
+
+export type GbvCourseOption = GbvVerificationArtifact & {
+ slug?: string;
+};
+
+export type RunFlowMessage = {
+ type: "GBV_RUN_FLOW";
+ artifact: GbvVerificationArtifact;
+};
+
+export type PingMessage = {
+ type: "GBV_PING";
+};
+
+export type CollectSurfaceMessage = {
+ type: "GBV_COLLECT_SURFACE";
+ requestId: string;
+ nonce: string;
+ nonceLeafId: string;
+};
+
+export type PageCollectRequest = {
+ type: "GBV_PAGE_COLLECT_REQUEST";
+ requestId: string;
+ nonce: string;
+ nonceLeafId: string;
+};
+
+export type PageSnapshot = {
+ url: string;
+ html: string;
+ injectedNonce: string;
+};
+
+export type PageCollectResponse =
+ | {
+ type: "GBV_PAGE_COLLECT_RESPONSE";
+ requestId: string;
+ ok: true;
+ snapshot: PageSnapshot;
+ }
+ | {
+ type: "GBV_PAGE_COLLECT_RESPONSE";
+ requestId: string;
+ ok: false;
+ error: string;
+ };
+
+export type CanonicalizedSurface = {
+ url: string;
+ html: string;
+ injectedNonce: string;
+ pageType: GbvPageType;
+ canonicalLeaves: string[];
+ nonceMetadata: { id: string; value: string };
+ observedAt: string;
+};
+
+export type CollectSurfaceResponse =
+ | {
+ ok: true;
+ surface: CanonicalizedSurface;
+ }
+ | {
+ ok: false;
+ error: string;
+ };
+
+export type RunFlowResult = {
+ status: number;
+ body: GbvSubmitResponsePayload;
+ artifact: GbvVerificationArtifact;
+ observationCount: number;
+ canonicalLeafCount: number;
+ sessionId: string;
+ startedAt: string;
+ finishedAt: string;
+ durationMs: number;
+};
+
+export type RunFlowResponse =
+ | {
+ ok: true;
+ result: RunFlowResult;
+ }
+ | {
+ ok: false;
+ error: string;
+ };
+
+export type PingResponse = {
+ ok: true;
+ serviceWorker: "ready";
+ ts: string;
+};
+
+export type GbvTraceSummaryStages = {
+ parseMs?: number;
+ canonicalizeMs?: number;
+ structureMs?: number;
+ semanticMs?: number;
+ commitMs?: number;
+ [key: string]: unknown;
+};
+
+export type GbvTraceSummary = {
+ requestId?: string;
+ durationMs?: number;
+ stages?: GbvTraceSummaryStages;
+ [key: string]: unknown;
+};
+
+export type GbvSemanticFindings = {
+ errors?: string[];
+ warnings?: string[];
+ info?: string[];
+ [key: string]: unknown;
+};
+
+export type GbvSemanticInvariant = {
+ id?: string;
+ status?: "pass" | "fail" | "warn";
+ detail?: string;
+ [key: string]: unknown;
+};
+
+export type GbvSemanticReport = {
+ ok?: boolean;
+ score?: number;
+ findings?: GbvSemanticFindings;
+ invariants?: GbvSemanticInvariant[];
+ [key: string]: unknown;
+};
+
+export type GbvVerificationLog = {
+ semantic?: GbvSemanticReport;
+ [key: string]: unknown;
+};
+
+export type GbvSubmitSuccessPayload = {
+ ok: true;
+ requestId?: string;
+ resultCode?: string;
+ sessionId?: string;
+ provider?: string;
+ evidenceHash?: string;
+ merkleRoot?: string;
+ receiptId?: string;
+ receipt?: string;
+ verifiedAt?: string;
+ challengeNonce?: string;
+ protocolVersion?: string;
+ traceSummary?: GbvTraceSummary;
+ verificationLog?: GbvVerificationLog;
+ failedInvariantIds?: string[];
+ code?: string;
+ meta?: Record;
+ [key: string]: unknown;
+};
+
+export type GbvSubmitFailureMeta = {
+ sessionId?: string;
+ traceSummary?: GbvTraceSummary;
+ verificationLog?: GbvVerificationLog;
+ failedInvariantIds?: string[];
+ [key: string]: unknown;
+};
+
+export type GbvSubmitFailurePayload = {
+ ok: false;
+ requestId?: string;
+ code?: string;
+ resultCode?: string;
+ error?: string;
+ meta?: GbvSubmitFailureMeta;
+ [key: string]: unknown;
+};
+
+export type GbvSubmitResponsePayload =
+ | GbvSubmitSuccessPayload
+ | GbvSubmitFailurePayload
+ | Record;
diff --git a/apps/extension/src/popup/App.tsx b/apps/extension/src/popup/App.tsx
new file mode 100644
index 0000000..d081aa9
--- /dev/null
+++ b/apps/extension/src/popup/App.tsx
@@ -0,0 +1,355 @@
+import { useEffect, useMemo, useState } from "react";
+import { gbvConfig } from "@gbv/config";
+import type { GbvCourseOption, RunFlowResponse, RunFlowResult } from "../lib/messages";
+import { normalizeVerifierResult, type NormalizedVerifierResult } from "./normalizeVerifierResult";
+
+type CatalogResponse =
+ | {
+ ok: true;
+ courses: GbvCourseOption[];
+ }
+ | {
+ ok: false;
+ error: string;
+ };
+
+function useCourseCatalog() {
+ const [courses, setCourses] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ let cancelled = false;
+
+ const load = async () => {
+ try {
+ setLoading(true);
+ setError("");
+
+ const response = await fetch(
+ `${gbvConfig.origins.syntheticClient}${gbvConfig.demo.courseCatalogApiPath}`,
+ );
+ const json = (await response.json()) as CatalogResponse;
+
+ if (!response.ok || !json.ok) {
+ throw new Error((json as { error?: string }).error || "Failed to fetch courses");
+ }
+ if (!Array.isArray(json.courses) || !json.courses.length) {
+ throw new Error("No courses were returned from synthetic catalog");
+ }
+
+ if (!cancelled) {
+ setCourses(json.courses);
+ }
+ } catch (fetchError) {
+ if (!cancelled) {
+ setError(String((fetchError as Error)?.message || fetchError));
+ }
+ } finally {
+ if (!cancelled) {
+ setLoading(false);
+ }
+ }
+ };
+
+ load();
+ return () => {
+ cancelled = true;
+ };
+ }, []);
+
+ return { courses, loading, error };
+}
+
+function toText(value: unknown): string {
+ if (value === undefined || value === null) return "-";
+ return String(value);
+}
+
+function metricCard(label: string, value: string, tone = "#0f172a") {
+ return (
+
+ );
+}
+
+function section(title: string, rows: Array<{ label: string; value: string }>) {
+ return (
+
+ {title}
+
+ {rows.map((row) => (
+
+
{row.label}
+
+ {row.value}
+
+
+ ))}
+
+
+ );
+}
+
+function commitDisplayValue(normalized: NormalizedVerifierResult, value: unknown): string {
+ if (normalized.commitStageExecuted) {
+ return toText(value);
+ }
+ return `Not executed (run terminated at ${normalized.terminationStage})`;
+}
+
+function formatCourseOptionLabel(course: GbvCourseOption): string {
+ const key = String(course.publicCourseKey || "").trim();
+ return key || "Unnamed course";
+}
+
+export default function App() {
+ const { courses, loading, error: catalogError } = useCourseCatalog();
+ const [selectedCourseKey, setSelectedCourseKey] = useState("");
+ const [running, setRunning] = useState(false);
+ const [result, setResult] = useState(null);
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ if (!selectedCourseKey && courses.length > 0) {
+ setSelectedCourseKey(courses[0].publicCourseKey);
+ }
+ }, [courses, selectedCourseKey]);
+
+ const selectedCourse = useMemo(
+ () => courses.find((course) => course.publicCourseKey === selectedCourseKey) || null,
+ [courses, selectedCourseKey],
+ );
+
+ const normalized = useMemo(
+ () => (result ? normalizeVerifierResult(result) : null),
+ [result],
+ );
+
+ const runFlow = () => {
+ if (!selectedCourse) return;
+
+ setRunning(true);
+ setError("");
+ setResult(null);
+
+ chrome.runtime.sendMessage(
+ { type: "GBV_RUN_FLOW", artifact: selectedCourse },
+ (response: RunFlowResponse) => {
+ setRunning(false);
+
+ if (chrome.runtime.lastError) {
+ setError(chrome.runtime.lastError.message ?? "Chrome runtime error");
+ return;
+ }
+
+ if (!response?.ok) {
+ setError(response?.error || "Unknown extension error");
+ return;
+ }
+
+ setResult(response.result);
+ },
+ );
+ };
+
+ const resolvedError = error || catalogError;
+ const decisionState = !normalized
+ ? ""
+ : normalized.httpStatus === 200
+ ? "Verified"
+ : normalized.terminationStage === "semantic"
+ ? "Mismatch"
+ : "Error";
+ const decisionTone =
+ decisionState === "Verified" ? "#166534" : decisionState === "Mismatch" ? "#991b1b" : "#9a3412";
+
+ return (
+
+ GBV Verifier Inspector
+
+ Course Artifact
+ setSelectedCourseKey(event.target.value)}
+ style={{ width: "100%", marginBottom: 12, padding: 8 }}
+ >
+ {courses.map((course) => (
+
+ {formatCourseOptionLabel(course)}
+
+ ))}
+
+
+
+ {running ? "Running GBV flow..." : "Verify Selected Artifact"}
+
+
+ {resolvedError ? (
+
+ {resolvedError}
+
+ ) : null}
+
+ {normalized ? (
+
+
+ Verification Summary
+
+ {decisionState}
+
+
+ {metricCard("HTTP Status", toText(normalized.httpStatus))}
+ {metricCard("Decision Code", normalized.decisionCode)}
+ {metricCard("Score", toText(normalized.score))}
+ {metricCard("Execution", `${toText(normalized.durationMs)} ms`)}
+ {metricCard("Session ID", normalized.sessionId)}
+ {metricCard("Request ID", normalized.requestId)}
+
+
+
+ {section("Challenge / Session", [
+ { label: "Artifact Key", value: normalized.artifact.publicCourseKey },
+ { label: "Course ID", value: toText(normalized.artifact.courseId) },
+ { label: "Session ID", value: normalized.sessionId },
+ { label: "Request ID", value: normalized.requestId },
+ { label: "Started", value: normalized.startedAt },
+ { label: "Finished", value: normalized.finishedAt },
+ { label: "Termination Stage", value: normalized.terminationStage },
+ ])}
+
+ {section("Pipeline Stages", [
+ {
+ label: "Parse",
+ value: `${normalized.stages.parse.executed} (${toText(normalized.stages.parse.durationMs)} ms)`,
+ },
+ {
+ label: "Canonicalize",
+ value: `${normalized.stages.canonicalize.executed} (${toText(normalized.stages.canonicalize.durationMs)} ms)`,
+ },
+ {
+ label: "Structure",
+ value: `${normalized.stages.structure.executed} (${toText(normalized.stages.structure.durationMs)} ms)`,
+ },
+ {
+ label: "Semantic",
+ value: `${normalized.stages.semantic.executed} (${toText(normalized.stages.semantic.durationMs)} ms)`,
+ },
+ {
+ label: "Commit",
+ value: `${normalized.stages.commit.executed} (${toText(normalized.stages.commit.durationMs)} ms)`,
+ },
+ ])}
+
+ {section("Evidence / Commitments", [
+ { label: "Observation Count", value: toText(normalized.observationCount) },
+ { label: "Canonical Leaf Count", value: toText(normalized.canonicalLeafCount) },
+ { label: "Evidence Hash", value: commitDisplayValue(normalized, normalized.evidenceHash) },
+ { label: "Merkle Root", value: commitDisplayValue(normalized, normalized.merkleRoot) },
+ { label: "Receipt ID", value: commitDisplayValue(normalized, normalized.receiptId) },
+ ])}
+
+ {section("Semantic Findings", [
+ { label: "Semantic Executed", value: toText(normalized.semantic.executed) },
+ { label: "Semantic OK", value: toText(normalized.semantic.ok) },
+ { label: "Score", value: toText(normalized.semantic.score) },
+ { label: "Result Code", value: normalized.decisionCode },
+ {
+ label: "Failed Checks",
+ value:
+ normalized.semantic.failedInvariantIds.length > 0
+ ? normalized.semantic.failedInvariantIds.join(" | ")
+ : "-",
+ },
+ {
+ label: "Semantic Errors",
+ value:
+ normalized.semantic.findings.errors.length > 0
+ ? normalized.semantic.findings.errors.join(" | ")
+ : "-",
+ },
+ ])}
+
+ {section("Receipt / Protocol Metadata", [
+ { label: "Provider", value: toText(normalized.provider) },
+ { label: "Verified At", value: commitDisplayValue(normalized, normalized.verifiedAt) },
+ { label: "Challenge Nonce", value: toText(normalized.challengeNonce) },
+ { label: "Protocol Version", value: toText(normalized.protocolVersion) },
+ { label: "Receipt Token", value: commitDisplayValue(normalized, normalized.receipt) },
+ ])}
+
+
+
+ Raw Response (verbatim server payload)
+
+
+ {JSON.stringify(normalized.rawResponse, null, 2)}
+
+
+
+ ) : null}
+
+ );
+}
diff --git a/apps/extension/src/popup/main.tsx b/apps/extension/src/popup/main.tsx
new file mode 100644
index 0000000..b33beaa
--- /dev/null
+++ b/apps/extension/src/popup/main.tsx
@@ -0,0 +1,9 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import App from "./App";
+
+createRoot(document.getElementById("root")!).render(
+
+
+ ,
+);
\ No newline at end of file
diff --git a/apps/extension/src/popup/normalizeVerifierResult.test.ts b/apps/extension/src/popup/normalizeVerifierResult.test.ts
new file mode 100644
index 0000000..e152326
--- /dev/null
+++ b/apps/extension/src/popup/normalizeVerifierResult.test.ts
@@ -0,0 +1,158 @@
+import assert from "node:assert/strict";
+import { describe, test } from "node:test";
+import type { RunFlowResult } from "../lib/messages";
+import { normalizeVerifierResult } from "./normalizeVerifierResult";
+
+function buildBaseResult(body: RunFlowResult["body"]): RunFlowResult {
+ return {
+ status: 400,
+ body,
+ artifact: {
+ courseId: 404,
+ publicCourseKey: "csk_t1mix",
+ certificateId: "MIX-7Q1Z-442K",
+ title: "Secure Evidence Practicum",
+ },
+ observationCount: 6,
+ canonicalLeafCount: 31,
+ sessionId: "fallback-session-id",
+ startedAt: "2026-02-14T00:00:00.000Z",
+ finishedAt: "2026-02-14T00:00:01.000Z",
+ durationMs: 1000,
+ };
+}
+
+describe("normalizeVerifierResult", () => {
+ test("treats zero-duration present stages as executed on success", () => {
+ const result = buildBaseResult({
+ ok: true,
+ requestId: "req-1",
+ sessionId: "sess-1",
+ resultCode: "VERIFICATION_ACCEPTED",
+ verificationLog: {
+ semantic: {
+ ok: true,
+ score: 100,
+ findings: { errors: [], warnings: [], info: [] },
+ invariants: [],
+ },
+ },
+ traceSummary: {
+ durationMs: 0,
+ stages: {
+ parseMs: 0,
+ canonicalizeMs: 0,
+ structureMs: 0,
+ semanticMs: 0,
+ commitMs: 0,
+ },
+ },
+ evidenceHash: "abc",
+ merkleRoot: "def",
+ receiptId: "rid-1",
+ receipt: "token",
+ });
+ result.status = 200;
+
+ const normalized = normalizeVerifierResult(result);
+ assert.equal(normalized.stages.parse.executed, true);
+ assert.equal(normalized.stages.semantic.executed, true);
+ assert.equal(normalized.stages.commit.executed, true);
+ assert.equal(normalized.commitStageExecuted, true);
+ assert.equal(normalized.terminationStage, "commit");
+ });
+
+ test("maps semantic-failure payload from meta and resolves semantic termination", () => {
+ const result = buildBaseResult({
+ ok: false,
+ code: "SEMANTIC_VERIFICATION_FAILED",
+ resultCode: "SEMANTIC_VERIFICATION_FAILED",
+ requestId: "req-2",
+ meta: {
+ sessionId: "sess-2",
+ traceSummary: {
+ durationMs: 12,
+ stages: {
+ parseMs: 3,
+ canonicalizeMs: 4,
+ structureMs: 5,
+ semanticMs: 0,
+ },
+ },
+ verificationLog: {
+ semantic: {
+ ok: false,
+ score: 55,
+ findings: { errors: ["grade_threshold"], warnings: [], info: [] },
+ invariants: [{ id: "grade_threshold", status: "fail" }],
+ },
+ },
+ failedInvariantIds: ["grade_threshold"],
+ },
+ });
+
+ const normalized = normalizeVerifierResult(result);
+ assert.equal(normalized.sessionId, "sess-2");
+ assert.equal(normalized.durationMs, 12);
+ assert.equal(normalized.semantic.executed, true);
+ assert.equal(normalized.semantic.ok, false);
+ assert.deepEqual(normalized.semantic.failedInvariantIds, ["grade_threshold"]);
+ assert.equal(normalized.commitStageExecuted, false);
+ assert.equal(normalized.terminationStage, "semantic");
+ });
+
+ test("resolves structure termination when semantic stage not present", () => {
+ const result = buildBaseResult({
+ ok: false,
+ code: "INJECTED_NONCE_MISMATCH",
+ requestId: "req-3",
+ meta: {
+ traceSummary: {
+ stages: {
+ parseMs: 1,
+ canonicalizeMs: 1,
+ structureMs: 1,
+ },
+ },
+ },
+ });
+
+ const normalized = normalizeVerifierResult(result);
+ assert.equal(normalized.stages.semantic.executed, false);
+ assert.equal(normalized.terminationStage, "structure");
+ assert.equal(normalized.commitStageExecuted, false);
+ });
+
+ test("always emits semantic object even when report missing", () => {
+ const result = buildBaseResult({
+ ok: false,
+ code: "SESSION_EXPIRED",
+ requestId: "req-4",
+ meta: {
+ traceSummary: {
+ stages: {
+ parseMs: 1,
+ },
+ },
+ },
+ });
+
+ const normalized = normalizeVerifierResult(result);
+ assert.equal(normalized.semantic.executed, false);
+ assert.deepEqual(normalized.semantic.findings.errors, []);
+ assert.deepEqual(normalized.semantic.failedInvariantIds, []);
+ assert.equal(normalized.terminationStage, "parse");
+ });
+
+ test("falls back to unknown_pre_trace when no stage map is available", () => {
+ const result = buildBaseResult({
+ ok: false,
+ code: "INTERNAL_ERROR",
+ requestId: "req-5",
+ });
+
+ const normalized = normalizeVerifierResult(result);
+ assert.equal(normalized.terminationStage, "unknown_pre_trace");
+ assert.equal(normalized.commitStageExecuted, false);
+ });
+});
diff --git a/apps/extension/src/popup/normalizeVerifierResult.ts b/apps/extension/src/popup/normalizeVerifierResult.ts
new file mode 100644
index 0000000..f89c0c7
--- /dev/null
+++ b/apps/extension/src/popup/normalizeVerifierResult.ts
@@ -0,0 +1,228 @@
+import type {
+ GbvSemanticInvariant,
+ GbvSubmitFailurePayload,
+ GbvSubmitResponsePayload,
+ GbvSubmitSuccessPayload,
+ GbvTraceSummary,
+ RunFlowResult,
+} from "../lib/messages";
+
+export type PipelineStageName =
+ | "parse"
+ | "canonicalize"
+ | "structure"
+ | "semantic"
+ | "commit";
+
+export type TerminationStage = PipelineStageName | "unknown_pre_trace";
+
+export type NormalizedStage = {
+ executed: boolean;
+ durationMs?: number;
+};
+
+export type NormalizedPipelineStages = {
+ parse: NormalizedStage;
+ canonicalize: NormalizedStage;
+ structure: NormalizedStage;
+ semantic: NormalizedStage;
+ commit: NormalizedStage;
+};
+
+export type NormalizedSemanticStage = {
+ executed: boolean;
+ ok?: boolean;
+ score?: number;
+ findings: {
+ errors: string[];
+ warnings: string[];
+ info: string[];
+ };
+ failedInvariantIds: string[];
+};
+
+export type NormalizedVerifierResult = {
+ httpStatus: number;
+ decisionCode: string;
+ requestId: string;
+ sessionId: string;
+ durationMs?: number;
+ score?: number;
+ stages: NormalizedPipelineStages;
+ semantic: NormalizedSemanticStage;
+ commitStageExecuted: boolean;
+ terminationStage: TerminationStage;
+ artifact: RunFlowResult["artifact"];
+ observationCount: number;
+ canonicalLeafCount: number;
+ startedAt: string;
+ finishedAt: string;
+ provider?: string;
+ verifiedAt?: string;
+ challengeNonce?: string;
+ protocolVersion?: string;
+ evidenceHash?: string;
+ merkleRoot?: string;
+ receiptId?: string;
+ receipt?: string;
+ rawResponse: GbvSubmitResponsePayload;
+};
+
+function asRecord(value: unknown): Record | null {
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
+ return value as Record;
+}
+
+function asString(value: unknown): string | undefined {
+ return typeof value === "string" && value.length > 0 ? value : undefined;
+}
+
+function asNumber(value: unknown): number | undefined {
+ return Number.isFinite(value) ? Number(value) : undefined;
+}
+
+function asStringArray(value: unknown): string[] {
+ if (!Array.isArray(value)) return [];
+ return value.map((entry) => String(entry));
+}
+
+function isFailurePayload(payload: GbvSubmitResponsePayload): payload is GbvSubmitFailurePayload {
+ return asRecord(payload)?.ok === false;
+}
+
+function isSuccessPayload(payload: GbvSubmitResponsePayload): payload is GbvSubmitSuccessPayload {
+ return asRecord(payload)?.ok === true;
+}
+
+function getTraceSummary(payload: GbvSubmitResponsePayload): GbvTraceSummary | undefined {
+ const root = asRecord(payload);
+ const rootTrace = asRecord(root?.traceSummary);
+ if (rootTrace) return rootTrace as GbvTraceSummary;
+ const meta = asRecord(root?.meta);
+ const metaTrace = asRecord(meta?.traceSummary);
+ if (metaTrace) return metaTrace as GbvTraceSummary;
+ return undefined;
+}
+
+function stageFromPresence(stagesRaw: Record | null, key: string): NormalizedStage {
+ if (!stagesRaw) return { executed: false };
+ const executed = Object.prototype.hasOwnProperty.call(stagesRaw, key);
+ return {
+ executed,
+ ...(executed ? { durationMs: asNumber(stagesRaw[key]) } : {}),
+ };
+}
+
+function deriveTerminationStage(stages: NormalizedPipelineStages): TerminationStage {
+ if (stages.commit.executed) return "commit";
+ if (stages.semantic.executed) return "semantic";
+ if (stages.structure.executed) return "structure";
+ if (stages.canonicalize.executed) return "canonicalize";
+ if (stages.parse.executed) return "parse";
+ return "unknown_pre_trace";
+}
+
+function extractSemanticReport(payload: GbvSubmitResponsePayload): Record | null {
+ const root = asRecord(payload);
+ const rootLog = asRecord(root?.verificationLog);
+ const rootSemantic = asRecord(rootLog?.semantic);
+ if (rootSemantic) return rootSemantic;
+ const meta = asRecord(root?.meta);
+ const metaLog = asRecord(meta?.verificationLog);
+ const metaSemantic = asRecord(metaLog?.semantic);
+ return metaSemantic;
+}
+
+function extractFailedInvariantIds(
+ payload: GbvSubmitResponsePayload,
+ semanticReport: Record | null,
+): string[] {
+ const root = asRecord(payload);
+ const topLevel = asStringArray(root?.failedInvariantIds);
+ if (topLevel.length > 0) return topLevel;
+
+ const meta = asRecord(root?.meta);
+ const metaLevel = asStringArray(meta?.failedInvariantIds);
+ if (metaLevel.length > 0) return metaLevel;
+
+ const invariants = Array.isArray(semanticReport?.invariants)
+ ? (semanticReport?.invariants as GbvSemanticInvariant[])
+ : [];
+ return invariants
+ .filter((entry) => entry?.status === "fail" && typeof entry?.id === "string")
+ .map((entry) => String(entry.id));
+}
+
+export function normalizeVerifierResult(result: RunFlowResult): NormalizedVerifierResult {
+ const payload = result.body;
+ const payloadRecord = asRecord(payload);
+ const traceSummary = getTraceSummary(payload);
+ const traceSummaryRecord = asRecord(traceSummary);
+ const traceStages = asRecord(traceSummaryRecord?.stages);
+
+ const stages: NormalizedPipelineStages = {
+ parse: stageFromPresence(traceStages, "parseMs"),
+ canonicalize: stageFromPresence(traceStages, "canonicalizeMs"),
+ structure: stageFromPresence(traceStages, "structureMs"),
+ semantic: stageFromPresence(traceStages, "semanticMs"),
+ commit: stageFromPresence(traceStages, "commitMs"),
+ };
+
+ const semanticReport = extractSemanticReport(payload);
+ const findings = asRecord(semanticReport?.findings);
+ const failedInvariantIds = extractFailedInvariantIds(payload, semanticReport);
+
+ const semantic: NormalizedSemanticStage = {
+ executed: stages.semantic.executed,
+ ok: typeof semanticReport?.ok === "boolean" ? Boolean(semanticReport.ok) : undefined,
+ score: asNumber(semanticReport?.score),
+ findings: {
+ errors: asStringArray(findings?.errors),
+ warnings: asStringArray(findings?.warnings),
+ info: asStringArray(findings?.info),
+ },
+ failedInvariantIds,
+ };
+
+ const meta = asRecord(payloadRecord?.meta);
+ const requestId =
+ asString(payloadRecord?.requestId) ??
+ asString(traceSummaryRecord?.requestId) ??
+ "-";
+ const sessionId =
+ asString(payloadRecord?.sessionId) ??
+ asString(meta?.sessionId) ??
+ result.sessionId;
+ const durationMs = asNumber(traceSummaryRecord?.durationMs) ?? result.durationMs;
+ const decisionCode =
+ asString(payloadRecord?.resultCode) ??
+ asString(payloadRecord?.code) ??
+ (isSuccessPayload(payload) ? "VERIFICATION_ACCEPTED" : isFailurePayload(payload) ? "VERIFICATION_FAILED" : "UNKNOWN");
+
+ return {
+ httpStatus: result.status,
+ decisionCode,
+ requestId,
+ sessionId,
+ durationMs,
+ score: semantic.score,
+ stages,
+ semantic,
+ commitStageExecuted: stages.commit.executed,
+ terminationStage: deriveTerminationStage(stages),
+ artifact: result.artifact,
+ observationCount: result.observationCount,
+ canonicalLeafCount: result.canonicalLeafCount,
+ startedAt: result.startedAt,
+ finishedAt: result.finishedAt,
+ provider: asString(payloadRecord?.provider),
+ verifiedAt: asString(payloadRecord?.verifiedAt),
+ challengeNonce: asString(payloadRecord?.challengeNonce),
+ protocolVersion: asString(payloadRecord?.protocolVersion),
+ evidenceHash: asString(payloadRecord?.evidenceHash),
+ merkleRoot: asString(payloadRecord?.merkleRoot),
+ receiptId: asString(payloadRecord?.receiptId),
+ receipt: asString(payloadRecord?.receipt),
+ rawResponse: payload,
+ };
+}
diff --git a/apps/extension/tsconfig.json b/apps/extension/tsconfig.json
new file mode 100644
index 0000000..494c1e9
--- /dev/null
+++ b/apps/extension/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "types": ["chrome", "node"]
+ },
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.tsx",
+ "vite.popup.config.ts",
+ "vite.background.config.ts",
+ "vite.content.config.ts"
+ ]
+}
diff --git a/apps/extension/vite.background.config.ts b/apps/extension/vite.background.config.ts
new file mode 100644
index 0000000..1666752
--- /dev/null
+++ b/apps/extension/vite.background.config.ts
@@ -0,0 +1,20 @@
+import { defineConfig } from "vite";
+import gbvConfig from "../../gbv.config";
+
+export default defineConfig({
+ build: {
+ outDir: gbvConfig.extension.buildDir.replace("apps/extension/", ""),
+ emptyOutDir: false,
+ lib: {
+ entry: "src/background.ts",
+ formats: ["iife"],
+ name: "GbvBackground",
+ fileName: () => "background.js",
+ },
+ rollupOptions: {
+ output: {
+ inlineDynamicImports: true,
+ },
+ },
+ },
+});
diff --git a/apps/extension/vite.content.config.ts b/apps/extension/vite.content.config.ts
new file mode 100644
index 0000000..2e98e53
--- /dev/null
+++ b/apps/extension/vite.content.config.ts
@@ -0,0 +1,20 @@
+import { defineConfig } from "vite";
+import gbvConfig from "../../gbv.config";
+
+export default defineConfig({
+ build: {
+ outDir: gbvConfig.extension.buildDir.replace("apps/extension/", ""),
+ emptyOutDir: false,
+ lib: {
+ entry: "src/contentScript.ts",
+ formats: ["iife"],
+ name: "GbvContentScript",
+ fileName: () => "contentScript.js",
+ },
+ rollupOptions: {
+ output: {
+ inlineDynamicImports: true,
+ },
+ },
+ },
+});
diff --git a/apps/extension/vite.popup.config.ts b/apps/extension/vite.popup.config.ts
new file mode 100644
index 0000000..ec00cda
--- /dev/null
+++ b/apps/extension/vite.popup.config.ts
@@ -0,0 +1,16 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react-swc";
+import gbvConfig from "../../gbv.config";
+
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ outDir: gbvConfig.extension.buildDir.replace("apps/extension/", ""),
+ emptyOutDir: true,
+ rollupOptions: {
+ input: {
+ popup: "index.html",
+ },
+ },
+ },
+});
diff --git a/apps/server/.gitignore b/apps/server/.gitignore
new file mode 100644
index 0000000..3f99cd0
--- /dev/null
+++ b/apps/server/.gitignore
@@ -0,0 +1,44 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# opt in .env.local
+!.env.example
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/apps/server/README.md b/apps/server/README.md
index 76ac08d..b7beaf4 100644
--- a/apps/server/README.md
+++ b/apps/server/README.md
@@ -1 +1,22 @@
-# Server
+# @gbv/server
+
+GBV verifier API application (Next.js app router).
+
+## Endpoints
+
+- `GET /api/health`
+- `POST /api/gbv/init`
+- `POST /api/gbv/submit`
+- `GET /api/gbv/receipt/:receiptId`
+
+## Local Commands
+
+From repo root:
+
+- `corepack pnpm --filter @gbv/server dev`
+- `corepack pnpm --filter @gbv/server test`
+- `corepack pnpm --filter @gbv/server typecheck`
+
+All runtime constants come from `gbv.config.ts`.
+
+See root `README.md` for full-stack setup and extension-driven demo flow.
diff --git a/apps/server/eslint.config.mjs b/apps/server/eslint.config.mjs
new file mode 100644
index 0000000..855b6cd
--- /dev/null
+++ b/apps/server/eslint.config.mjs
@@ -0,0 +1,7 @@
+import { defineConfig, globalIgnores } from "eslint/config";
+import nextVitals from "eslint-config-next/core-web-vitals";
+
+export default defineConfig([
+ ...nextVitals,
+ globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
+]);
\ No newline at end of file
diff --git a/apps/server/next.config.mjs b/apps/server/next.config.mjs
new file mode 100644
index 0000000..3325d9f
--- /dev/null
+++ b/apps/server/next.config.mjs
@@ -0,0 +1,7 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+ transpilePackages: ["@gbv/config", "@gbv/core"],
+};
+
+export default nextConfig;
\ No newline at end of file
diff --git a/apps/server/package.json b/apps/server/package.json
new file mode 100644
index 0000000..21fe2b1
--- /dev/null
+++ b/apps/server/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@gbv/server",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "tsx scripts/dev.ts",
+ "build": "next build",
+ "start": "tsx scripts/start.ts",
+ "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
+ "typecheck": "tsc -p tsconfig.json",
+ "test": "tsx --test tests/**/*.test.ts"
+ },
+ "dependencies": {
+ "@gbv/config": "workspace:*",
+ "@gbv/core": "workspace:*",
+ "next": "16.1.1",
+ "react": "19.2.3",
+ "react-dom": "19.2.3"
+ },
+ "devDependencies": {
+ "eslint": "^9.38.0",
+ "eslint-config-next": "16.1.1"
+ }
+}
diff --git a/apps/server/scripts/dev.ts b/apps/server/scripts/dev.ts
new file mode 100644
index 0000000..1b98e88
--- /dev/null
+++ b/apps/server/scripts/dev.ts
@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+import { spawn } from "node:child_process";
+import { gbvConfig } from "@gbv/config";
+
+const child = spawn(
+ "corepack",
+ ["pnpm", "exec", "next", "dev", "-p", String(gbvConfig.ports.server)],
+ {
+ stdio: "inherit",
+ shell: process.platform === "win32",
+ env: { ...process.env, PORT: String(gbvConfig.ports.server) },
+ },
+);
+
+child.on("exit", (code) => process.exit(code ?? 0));
diff --git a/apps/server/scripts/start.ts b/apps/server/scripts/start.ts
new file mode 100644
index 0000000..ac79dc5
--- /dev/null
+++ b/apps/server/scripts/start.ts
@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+import { spawn } from "node:child_process";
+import { gbvConfig } from "@gbv/config";
+
+const child = spawn(
+ "corepack",
+ ["pnpm", "exec", "next", "start", "-p", String(gbvConfig.ports.server)],
+ {
+ stdio: "inherit",
+ shell: process.platform === "win32",
+ env: { ...process.env, PORT: String(gbvConfig.ports.server) },
+ },
+);
+
+child.on("exit", (code) => process.exit(code ?? 0));
diff --git a/apps/server/src/app/api/gbv/init/route.ts b/apps/server/src/app/api/gbv/init/route.ts
new file mode 100644
index 0000000..74bdbcd
--- /dev/null
+++ b/apps/server/src/app/api/gbv/init/route.ts
@@ -0,0 +1,8 @@
+import { NextResponse } from "next/server";
+import { initGbvSession } from "@/lib/gbvServerService";
+
+export async function POST(req: Request) {
+ const body = await req.json().catch(() => ({}));
+ const response = initGbvSession(body);
+ return NextResponse.json(response.body, { status: response.status });
+}
\ No newline at end of file
diff --git a/apps/server/src/app/api/gbv/receipt/[receiptId]/route.ts b/apps/server/src/app/api/gbv/receipt/[receiptId]/route.ts
new file mode 100644
index 0000000..c1170f8
--- /dev/null
+++ b/apps/server/src/app/api/gbv/receipt/[receiptId]/route.ts
@@ -0,0 +1,11 @@
+import { NextResponse } from "next/server";
+import { getGbvReceipt } from "@/lib/gbvServerService";
+
+export async function GET(
+ _req: Request,
+ context: { params: Promise<{ receiptId: string }> },
+) {
+ const params = await context.params;
+ const response = getGbvReceipt(params.receiptId);
+ return NextResponse.json(response.body, { status: response.status });
+}
\ No newline at end of file
diff --git a/apps/server/src/app/api/gbv/submit/route.ts b/apps/server/src/app/api/gbv/submit/route.ts
new file mode 100644
index 0000000..5a5f167
--- /dev/null
+++ b/apps/server/src/app/api/gbv/submit/route.ts
@@ -0,0 +1,8 @@
+import { NextResponse } from "next/server";
+import { submitGbvEvidence } from "@/lib/gbvServerService";
+
+export async function POST(req: Request) {
+ const body = await req.json().catch(() => ({}));
+ const response = submitGbvEvidence(body);
+ return NextResponse.json(response.body, { status: response.status });
+}
\ No newline at end of file
diff --git a/apps/server/src/app/api/health/route.ts b/apps/server/src/app/api/health/route.ts
new file mode 100644
index 0000000..ba1ff33
--- /dev/null
+++ b/apps/server/src/app/api/health/route.ts
@@ -0,0 +1,10 @@
+import { NextResponse } from "next/server";
+
+export async function GET() {
+ return NextResponse.json({
+ ok: true,
+ status: "ready",
+ service: "gbv-server",
+ timestamp: new Date().toISOString(),
+ });
+}
\ No newline at end of file
diff --git a/apps/server/src/app/api/hello/route.ts b/apps/server/src/app/api/hello/route.ts
new file mode 100644
index 0000000..ce99035
--- /dev/null
+++ b/apps/server/src/app/api/hello/route.ts
@@ -0,0 +1,6 @@
+export async function GET() {
+ return new Response("Hello!", {
+ status: 200,
+ headers: { "Content-Type": "text/plain; charset=utf-8" },
+ });
+}
diff --git a/apps/server/src/lib/gbvServerService.ts b/apps/server/src/lib/gbvServerService.ts
new file mode 100644
index 0000000..d40f680
--- /dev/null
+++ b/apps/server/src/lib/gbvServerService.ts
@@ -0,0 +1,545 @@
+import crypto from "node:crypto";
+import {
+ buildMerkleRoot,
+ type GbvPageBundle,
+ type GbvPageType,
+ type GbvReceiptPayload,
+ type GbvSemanticReport,
+ type GbvVerificationArtifact,
+ sha256Hex,
+ signReceipt,
+ syntheticProvider,
+ verifyProviderConsistency,
+ verifyStructureBoundNonces,
+} from "@gbv/core";
+import { gbvConfig } from "@gbv/config";
+import { logDebug, logError, logInfo } from "./logger";
+import {
+ getReceipt,
+ getSession,
+ insertReceipt,
+ markSessionUsed,
+ upsertSession,
+ type GbvSessionRecord,
+} from "./store/memoryStore";
+
+type VerificationLog = {
+ structure: { ok: boolean; error?: string; details?: Record };
+ semantic?: GbvSemanticReport;
+};
+
+type TraceSummary = {
+ requestId: string;
+ durationMs: number;
+ stages: {
+ parseMs: number;
+ canonicalizeMs: number;
+ structureMs: number;
+ semanticMs: number;
+ commitMs: number;
+ };
+};
+
+class GbvServiceError extends Error {
+ status: number;
+ code: string;
+ meta?: Record;
+
+ constructor(
+ status: number,
+ code: string,
+ message: string,
+ meta?: Record,
+ ) {
+ super(message);
+ this.status = status;
+ this.code = code;
+ this.meta = meta;
+ }
+}
+
+function parseBody(body: unknown): Record {
+ if (body && typeof body === "object") {
+ return body as Record;
+ }
+ return {};
+}
+
+function parseArtifact(rawArtifact: unknown): GbvVerificationArtifact {
+ const artifact = rawArtifact as Record;
+ const courseId = Number(artifact?.courseId);
+ const publicCourseKey = String(artifact?.publicCourseKey || "").trim();
+ const certificateId = String(artifact?.certificateId || "").trim();
+ const title = String(artifact?.title || "").trim();
+
+ if (!Number.isFinite(courseId)) {
+ throw new GbvServiceError(400, "INVALID_ARTIFACT", "artifact.courseId must be numeric");
+ }
+ if (!publicCourseKey) {
+ throw new GbvServiceError(400, "INVALID_ARTIFACT", "artifact.publicCourseKey is required");
+ }
+ if (!certificateId) {
+ throw new GbvServiceError(400, "INVALID_ARTIFACT", "artifact.certificateId is required");
+ }
+
+ return {
+ courseId,
+ publicCourseKey,
+ certificateId,
+ ...(title ? { title } : {}),
+ };
+}
+
+function parseSubmitPages(rawPages: unknown): Array<{ url: string; html: string; injectedNonce: string }> {
+ if (!Array.isArray(rawPages) || rawPages.length === 0) {
+ throw new GbvServiceError(400, "INVALID_PAGES", "pages[] must be a non-empty array");
+ }
+
+ if (rawPages.length > gbvConfig.protocol.maxPages) {
+ throw new GbvServiceError(400, "TOO_MANY_PAGES", "Too many pages submitted", {
+ maxPages: gbvConfig.protocol.maxPages,
+ actual: rawPages.length,
+ });
+ }
+
+ return rawPages.map((entry, index) => {
+ const page = entry as Record;
+ const url = String(page?.url || "").trim();
+ const html = String(page?.html || "");
+ const injectedNonce = String(page?.injectedNonce || "").trim();
+
+ if (!url || !html || !injectedNonce) {
+ throw new GbvServiceError(
+ 400,
+ "INVALID_PAGE_ENTRY",
+ "Each page must contain url, html and injectedNonce",
+ { index },
+ );
+ }
+
+ if (html.length > gbvConfig.protocol.maxHtmlChars) {
+ throw new GbvServiceError(400, "HTML_TOO_LARGE", "Page html exceeds configured size limit", {
+ index,
+ maxHtmlChars: gbvConfig.protocol.maxHtmlChars,
+ });
+ }
+
+ return { url, html, injectedNonce };
+ });
+}
+
+function canonicalizePages(
+ pages: Array<{ url: string; html: string; injectedNonce: string }>,
+): GbvPageBundle[] {
+ return pages.map((page, index) => {
+ const pageType = syntheticProvider.detectPageType(page.url);
+ const leaves = syntheticProvider.canonicalize({
+ html: page.html,
+ url: page.url,
+ nonceLeafId: gbvConfig.protocol.nonceLeafId,
+ });
+
+ return {
+ index,
+ pageType,
+ url: page.url,
+ html: page.html,
+ injectedNonce: page.injectedNonce,
+ leaves,
+ };
+ });
+}
+
+function commitEvidence(bundles: GbvPageBundle[]): {
+ canonicalLeaves: string[];
+ evidenceHash: string;
+ merkleRoot: string;
+} {
+ const canonicalLeaves = [...new Set(bundles.flatMap((bundle) => bundle.leaves))].sort();
+
+ if (!canonicalLeaves.length) {
+ throw new GbvServiceError(400, "NO_CANONICAL_LEAVES", "No canonical leaves produced");
+ }
+
+ const leafHashes = canonicalLeaves.map((leaf) => sha256Hex(leaf));
+ const merkleRoot = buildMerkleRoot(leafHashes);
+ const evidenceHash = sha256Hex(JSON.stringify(canonicalLeaves));
+
+ return { canonicalLeaves, evidenceHash, merkleRoot };
+}
+
+function requireSession(sessionId: string): GbvSessionRecord {
+ const session = getSession(sessionId);
+ if (!session) {
+ throw new GbvServiceError(404, "SESSION_NOT_FOUND", "Unknown sessionId");
+ }
+
+ if (session.used) {
+ throw new GbvServiceError(409, "SESSION_USED", "Session was already consumed");
+ }
+
+ if (Date.now() > Date.parse(session.expiresAt)) {
+ throw new GbvServiceError(403, "SESSION_EXPIRED", "Session has expired");
+ }
+
+ return session;
+}
+
+function errorResponse(error: unknown, requestId?: string): {
+ status: number;
+ body: {
+ ok: false;
+ requestId: string;
+ code: string;
+ resultCode: string;
+ error: string;
+ meta?: Record;
+ };
+} {
+ const resolvedRequestId = requestId || crypto.randomUUID();
+ if (error instanceof GbvServiceError) {
+ logInfo("request.failed", {
+ requestId: resolvedRequestId,
+ code: error.code,
+ status: error.status,
+ });
+ return {
+ status: error.status,
+ body: {
+ ok: false,
+ requestId: resolvedRequestId,
+ code: error.code,
+ resultCode: error.code,
+ error: error.message,
+ meta: error.meta,
+ },
+ };
+ }
+
+ logError("request.unhandled_error", {
+ requestId: resolvedRequestId,
+ });
+ return {
+ status: 500,
+ body: {
+ ok: false,
+ requestId: resolvedRequestId,
+ code: "INTERNAL_ERROR",
+ resultCode: "INTERNAL_ERROR",
+ error: "Unexpected GBV server error",
+ },
+ };
+}
+
+/** Spec Stage I: challenge issuance with verifier-side blindness to dataset validity. */
+export function initGbvSession(rawBody: unknown): {
+ status: number;
+ body: Record;
+} {
+ const requestId = crypto.randomUUID();
+ const startedAt = Date.now();
+ try {
+ const body = parseBody(rawBody);
+ const artifact = parseArtifact(body.artifact);
+ const pagePlan = [...gbvConfig.demo.pagePlan] as GbvPageType[];
+ const pageNonces = pagePlan.map(() => crypto.randomUUID());
+ const sessionId = crypto.randomUUID();
+ const challengeNonce = crypto.randomUUID();
+ const expiresAt = new Date(Date.now() + gbvConfig.protocol.sessionTtlMs).toISOString();
+
+ const record: GbvSessionRecord = {
+ sessionId,
+ challengeNonce,
+ provider: gbvConfig.protocol.providerId,
+ artifact,
+ pagePlan,
+ pageNonces,
+ nonceLeafId: gbvConfig.protocol.nonceLeafId,
+ expiresAt,
+ used: false,
+ };
+
+ upsertSession(record);
+ const durationMs = Date.now() - startedAt;
+ logInfo("stage.init.completed", {
+ requestId,
+ durationMs,
+ sessionId,
+ artifactKey: artifact.publicCourseKey,
+ });
+
+ return {
+ status: 200,
+ body: {
+ ok: true,
+ requestId,
+ resultCode: "SESSION_INITIALIZED",
+ sessionId,
+ challengeNonce,
+ provider: record.provider,
+ artifact,
+ pagePlan,
+ pageNonces,
+ nonceLeafId: record.nonceLeafId,
+ expiresAt,
+ timing: { totalMs: durationMs },
+ },
+ };
+ } catch (error) {
+ return errorResponse(error, requestId);
+ }
+}
+
+/** Spec Stage II-V: verify only from observed surfaces and issued nonce plan. */
+export function submitGbvEvidence(rawBody: unknown): {
+ status: number;
+ body: Record;
+} {
+ const requestId = crypto.randomUUID();
+ const startedAt = Date.now();
+ try {
+ const parseStart = Date.now();
+ const body = parseBody(rawBody);
+ const sessionId = String(body.sessionId || "").trim();
+ const provider = String(body.provider || "").trim().toLowerCase();
+ const courseId = Number(body.courseId);
+ const pages = parseSubmitPages(body.pages);
+ const parseMs = Date.now() - parseStart;
+
+ if (!sessionId) {
+ throw new GbvServiceError(400, "MISSING_SESSION_ID", "sessionId is required");
+ }
+ if (!provider) {
+ throw new GbvServiceError(400, "MISSING_PROVIDER", "provider is required");
+ }
+ if (!Number.isFinite(courseId)) {
+ throw new GbvServiceError(400, "INVALID_COURSE_ID", "courseId must be numeric");
+ }
+
+ const session = requireSession(sessionId);
+ if (provider !== session.provider) {
+ throw new GbvServiceError(
+ 400,
+ "PROVIDER_MISMATCH",
+ "Provider does not match issued session",
+ {
+ expected: session.provider,
+ provider,
+ },
+ );
+ }
+ if (courseId !== session.artifact.courseId) {
+ throw new GbvServiceError(
+ 400,
+ "COURSE_MISMATCH",
+ "courseId does not match issued session artifact",
+ );
+ }
+ if (pages.length !== session.pagePlan.length) {
+ throw new GbvServiceError(
+ 400,
+ "PAGE_COUNT_MISMATCH",
+ "Submitted page count does not match the issued plan",
+ {
+ expected: session.pagePlan.length,
+ actual: pages.length,
+ },
+ );
+ }
+
+ logDebug("stage.submit.input_validated", {
+ requestId,
+ sessionId,
+ provider,
+ courseId,
+ pageCount: pages.length,
+ });
+
+ const canonicalizeStart = Date.now();
+ const bundles = canonicalizePages(pages);
+ const canonicalizeMs = Date.now() - canonicalizeStart;
+ const verificationLog: VerificationLog = {
+ structure: { ok: true },
+ };
+
+ const structureStart = Date.now();
+ const structure = verifyStructureBoundNonces({
+ bundles,
+ pagePlan: session.pagePlan,
+ pageNonces: session.pageNonces,
+ nonceLeafId: session.nonceLeafId,
+ });
+ const structureMs = Date.now() - structureStart;
+
+ if (!structure.ok) {
+ verificationLog.structure = {
+ ok: false,
+ error: structure.error,
+ details: structure.details,
+ };
+ const traceSummary: TraceSummary = {
+ requestId,
+ durationMs: Date.now() - startedAt,
+ stages: {
+ parseMs,
+ canonicalizeMs,
+ structureMs,
+ semanticMs: 0,
+ commitMs: 0,
+ },
+ };
+ throw new GbvServiceError(
+ 400,
+ structure.error,
+ "Structure-bound nonce verification failed",
+ { verificationLog, traceSummary, sessionId },
+ );
+ }
+
+ const semanticStart = Date.now();
+ const semantic = verifyProviderConsistency({
+ provider,
+ bundles,
+ minGradePercent: gbvConfig.protocol.minGradePercent,
+ expectedNonces: session.pageNonces.map((value) => ({
+ id: session.nonceLeafId,
+ value,
+ })),
+ });
+ const semanticMs = Date.now() - semanticStart;
+ verificationLog.semantic = semantic;
+
+ if (!semantic.ok) {
+ const failedInvariantIds = semantic.invariants
+ .filter((entry) => entry.status === "fail")
+ .map((entry) => entry.id);
+ const traceSummary: TraceSummary = {
+ requestId,
+ durationMs: Date.now() - startedAt,
+ stages: {
+ parseMs,
+ canonicalizeMs,
+ structureMs,
+ semanticMs,
+ commitMs: 0,
+ },
+ };
+ throw new GbvServiceError(
+ 400,
+ "SEMANTIC_VERIFICATION_FAILED",
+ "Semantic verification failed",
+ { verificationLog, failedInvariantIds, traceSummary, sessionId },
+ );
+ }
+
+ const commitStart = Date.now();
+ const { canonicalLeaves, evidenceHash, merkleRoot } = commitEvidence(bundles);
+ const commitMs = Date.now() - commitStart;
+ const receiptId = crypto.randomUUID();
+ const issuedAt = new Date().toISOString();
+ const durationMs = Date.now() - startedAt;
+
+ const payload: GbvReceiptPayload = {
+ receiptId,
+ sessionId,
+ artifact: session.artifact,
+ provider,
+ merkleRoot,
+ evidenceHash,
+ issuedAt,
+ protocolVersion: gbvConfig.versions.protocol,
+ };
+
+ const token = signReceipt(payload, gbvConfig.protocol.receiptHmacSecret);
+ insertReceipt(receiptId, { token, payload });
+ markSessionUsed(sessionId);
+ const failedInvariantIds = verificationLog.semantic?.invariants
+ .filter((entry) => entry.status === "fail")
+ .map((entry) => entry.id) || [];
+ const traceSummary: TraceSummary = {
+ requestId,
+ durationMs,
+ stages: {
+ parseMs,
+ canonicalizeMs,
+ structureMs,
+ semanticMs,
+ commitMs,
+ },
+ };
+
+ logInfo("stage.submit.completed", {
+ requestId,
+ sessionId,
+ status: 200,
+ leafCount: canonicalLeaves.length,
+ durationMs,
+ });
+
+ return {
+ status: 200,
+ body: {
+ ok: true,
+ requestId,
+ resultCode: "VERIFICATION_ACCEPTED",
+ accepted: true,
+ sessionId,
+ receiptId,
+ receipt: token,
+ provider,
+ artifact: session.artifact,
+ evidenceHash,
+ merkleRoot,
+ leafCount: canonicalLeaves.length,
+ verifiedAt: issuedAt,
+ verificationLog,
+ failedInvariantIds,
+ traceSummary,
+ timing: { totalMs: durationMs },
+ },
+ };
+ } catch (error) {
+ return errorResponse(error, requestId);
+ }
+}
+
+/** Retrieve verifier receipt by ID. */
+export function getGbvReceipt(receiptId: string): {
+ status: number;
+ body: Record;
+} {
+ const requestId = crypto.randomUUID();
+ const startedAt = Date.now();
+ try {
+ const normalizedId = String(receiptId || "").trim();
+ if (!normalizedId) {
+ throw new GbvServiceError(400, "MISSING_RECEIPT_ID", "receiptId is required");
+ }
+
+ const receipt = getReceipt(normalizedId);
+ if (!receipt) {
+ throw new GbvServiceError(404, "RECEIPT_NOT_FOUND", "Receipt not found");
+ }
+ const durationMs = Date.now() - startedAt;
+ logInfo("stage.receipt.completed", {
+ requestId,
+ receiptId: normalizedId,
+ durationMs,
+ });
+
+ return {
+ status: 200,
+ body: {
+ ok: true,
+ requestId,
+ resultCode: "RECEIPT_RETRIEVED",
+ receiptId: normalizedId,
+ receipt: receipt.token,
+ payload: receipt.payload,
+ timing: { totalMs: durationMs },
+ },
+ };
+ } catch (error) {
+ return errorResponse(error, requestId);
+ }
+}
diff --git a/apps/server/src/lib/logger.ts b/apps/server/src/lib/logger.ts
new file mode 100644
index 0000000..02bacd4
--- /dev/null
+++ b/apps/server/src/lib/logger.ts
@@ -0,0 +1,40 @@
+import { gbvConfig } from "@gbv/config";
+
+const debugEnabled = Boolean(gbvConfig.environment.debug);
+
+export function logInfo(event: string, meta: Record): void {
+ console.log(
+ JSON.stringify({
+ level: "info",
+ service: "gbv-server",
+ event,
+ ts: new Date().toISOString(),
+ ...meta,
+ }),
+ );
+}
+
+export function logError(event: string, meta: Record): void {
+ console.error(
+ JSON.stringify({
+ level: "error",
+ service: "gbv-server",
+ event,
+ ts: new Date().toISOString(),
+ ...meta,
+ }),
+ );
+}
+
+export function logDebug(event: string, meta: Record): void {
+ if (!debugEnabled) return;
+ console.debug(
+ JSON.stringify({
+ level: "debug",
+ service: "gbv-server",
+ event,
+ ts: new Date().toISOString(),
+ ...meta,
+ }),
+ );
+}
diff --git a/apps/server/src/lib/store/memoryStore.ts b/apps/server/src/lib/store/memoryStore.ts
new file mode 100644
index 0000000..d12c5a0
--- /dev/null
+++ b/apps/server/src/lib/store/memoryStore.ts
@@ -0,0 +1,65 @@
+import type { GbvReceiptPayload, GbvSession } from "@gbv/core";
+
+export interface GbvSessionRecord extends GbvSession {}
+
+export interface GbvReceiptRecord {
+ token: string;
+ payload: GbvReceiptPayload;
+}
+
+interface MemoryStore {
+ sessions: Map;
+ receipts: Map;
+}
+
+const globalStore = globalThis as typeof globalThis & { __GBV_MEMORY_STORE__?: MemoryStore };
+
+function getStore(): MemoryStore {
+ if (!globalStore.__GBV_MEMORY_STORE__) {
+ globalStore.__GBV_MEMORY_STORE__ = {
+ sessions: new Map(),
+ receipts: new Map(),
+ };
+ }
+ return globalStore.__GBV_MEMORY_STORE__;
+}
+
+export function upsertSession(record: GbvSessionRecord): void {
+ getStore().sessions.set(record.sessionId, record);
+}
+
+export function getSession(sessionId: string): GbvSessionRecord | undefined {
+ return getStore().sessions.get(sessionId);
+}
+
+export function markSessionUsed(sessionId: string): void {
+ const store = getStore();
+ const record = store.sessions.get(sessionId);
+ if (!record) return;
+ store.sessions.set(sessionId, { ...record, used: true });
+}
+
+export function insertReceipt(receiptId: string, record: GbvReceiptRecord): void {
+ getStore().receipts.set(receiptId, record);
+}
+
+export function getReceipt(receiptId: string): GbvReceiptRecord | undefined {
+ return getStore().receipts.get(receiptId);
+}
+
+export function resetMemoryStore(): void {
+ const store = getStore();
+ store.sessions.clear();
+ store.receipts.clear();
+}
+
+// Test-only helper for deterministic expiry validation.
+export function expireSessionNow(sessionId: string): void {
+ const store = getStore();
+ const record = store.sessions.get(sessionId);
+ if (!record) return;
+ store.sessions.set(sessionId, {
+ ...record,
+ expiresAt: new Date(Date.now() - 1000).toISOString(),
+ });
+}
diff --git a/apps/server/tests/api-contract.test.ts b/apps/server/tests/api-contract.test.ts
new file mode 100644
index 0000000..88c5786
--- /dev/null
+++ b/apps/server/tests/api-contract.test.ts
@@ -0,0 +1,245 @@
+import assert from "node:assert/strict";
+import { beforeEach, describe, test } from "node:test";
+
+import {
+ buildSurfaceUrlsFromPlan,
+ type GbvPageType,
+ type GbvVerificationArtifact,
+ verifyReceipt,
+} from "@gbv/core";
+import { gbvConfig } from "@gbv/config";
+import { POST as initRoute } from "../src/app/api/gbv/init/route";
+import { POST as submitRoute } from "../src/app/api/gbv/submit/route";
+import { expireSessionNow, resetMemoryStore } from "../src/lib/store/memoryStore";
+
+interface InitResponse {
+ ok: boolean;
+ sessionId: string;
+ provider: string;
+ artifact: GbvVerificationArtifact;
+ pagePlan: string[];
+ pageNonces: string[];
+}
+
+const demoArtifact: GbvVerificationArtifact = {
+ courseId: 101,
+ publicCourseKey: "csk_7r2q9p",
+ certificateId: "O04CRAKZMLZJ",
+ title: "Getting Started with Microsoft Excel",
+};
+
+async function postJson(
+ handler: (req: Request) => Promise,
+ path: string,
+ body: unknown,
+): Promise<{ status: number; json: any }> {
+ const req = new Request(`http://localhost${path}`, {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify(body),
+ });
+
+ const res = await handler(req);
+ return {
+ status: res.status,
+ json: await res.json(),
+ };
+}
+
+function createSemanticHtml(params: {
+ pageType: string;
+ nonce: string;
+ url: string;
+ gradePercent: number;
+ moduleCount: number;
+ courseName: string;
+ courseKey: string;
+ courseSlug: string;
+ certificateId: string;
+ courseId: number;
+}): string {
+ return `
+
+
+
+
+ ${params.pageType}
+
+
+
+
+ ${params.pageType}
+ Grade Achieved: ${params.gradePercent}%
+ URL: ${params.url}
+
+`;
+}
+
+function buildPages(
+ init: InitResponse,
+ options: {
+ gradePercent: number;
+ moduleCount?: number;
+ certificateIdOverrideByPage?: Record;
+ },
+) {
+ const urls = buildSurfaceUrlsFromPlan({
+ origin: gbvConfig.origins.syntheticClient,
+ pagePlan: init.pagePlan as GbvPageType[],
+ templates: gbvConfig.demo.surfacePathTemplates,
+ artifact: init.artifact,
+ });
+
+ return urls.map((url, index) => {
+ const pageType = init.pagePlan[index];
+ const certificateId =
+ options.certificateIdOverrideByPage?.[pageType] ?? init.artifact.certificateId;
+
+ return {
+ url,
+ injectedNonce: init.pageNonces[index],
+ html: createSemanticHtml({
+ pageType,
+ nonce: init.pageNonces[index],
+ url,
+ gradePercent: options.gradePercent,
+ moduleCount: options.moduleCount ?? 5,
+ courseName: init.artifact.title || "Synthetic Course",
+ courseKey: init.artifact.publicCourseKey,
+ courseSlug: "synthetic-slug",
+ certificateId,
+ courseId: init.artifact.courseId,
+ }),
+ };
+ });
+}
+
+describe("GBV API contract", () => {
+ beforeEach(() => {
+ resetMemoryStore();
+ });
+
+ test("POST /api/gbv/init returns a session and nonce plan", async () => {
+ const result = await postJson(initRoute, "/api/gbv/init", {
+ artifact: demoArtifact,
+ });
+
+ assert.equal(result.status, 200);
+ assert.equal(result.json.ok, true);
+ assert.ok(result.json.sessionId);
+ assert.equal(result.json.pagePlan.length, result.json.pageNonces.length);
+ assert.equal(result.json.artifact.publicCourseKey, demoArtifact.publicCourseKey);
+ });
+
+ test("POST /api/gbv/init rejects invalid artifact", async () => {
+ const result = await postJson(initRoute, "/api/gbv/init", {
+ artifact: {
+ courseId: 42,
+ publicCourseKey: "missing-certificate-id",
+ },
+ });
+
+ assert.equal(result.status, 400);
+ assert.equal(result.json.ok, false);
+ assert.equal(result.json.code, "INVALID_ARTIFACT");
+ });
+
+ test("POST /api/gbv/submit accepts coherent evidence", async () => {
+ const init = await postJson(initRoute, "/api/gbv/init", {
+ artifact: demoArtifact,
+ });
+
+ const initJson = init.json as InitResponse;
+ const submit = await postJson(submitRoute, "/api/gbv/submit", {
+ sessionId: initJson.sessionId,
+ courseId: demoArtifact.courseId,
+ provider: "synthetic",
+ pages: buildPages(initJson, { gradePercent: 100, moduleCount: 5 }),
+ });
+
+ assert.equal(submit.status, 200);
+ assert.equal(submit.json.ok, true);
+ assert.equal(submit.json.accepted, true);
+ assert.ok(submit.json.receipt);
+ assert.ok(submit.json.merkleRoot);
+ const receiptPayload = verifyReceipt(submit.json.receipt, gbvConfig.protocol.receiptHmacSecret);
+ assert.equal(receiptPayload.sessionId, initJson.sessionId);
+ assert.equal(receiptPayload.artifact.publicCourseKey, demoArtifact.publicCourseKey);
+ assert.equal(submit.json.verificationLog.structure.ok, true);
+ assert.equal(submit.json.verificationLog.semantic.ok, true);
+ });
+
+ test("POST /api/gbv/submit rejects semantic contradiction", async () => {
+ const init = await postJson(initRoute, "/api/gbv/init", {
+ artifact: demoArtifact,
+ });
+
+ const initJson = init.json as InitResponse;
+ const submit = await postJson(submitRoute, "/api/gbv/submit", {
+ sessionId: initJson.sessionId,
+ courseId: demoArtifact.courseId,
+ provider: "synthetic",
+ pages: buildPages(initJson, {
+ gradePercent: 100,
+ certificateIdOverrideByPage: {
+ proof: `${demoArtifact.certificateId}-ALT`,
+ },
+ }),
+ });
+
+ assert.equal(submit.status, 400);
+ assert.equal(submit.json.ok, false);
+ assert.equal(submit.json.code, "SEMANTIC_VERIFICATION_FAILED");
+ assert.equal(submit.json.meta.verificationLog.semantic.ok, false);
+ assert.ok(Array.isArray(submit.json.meta.failedInvariantIds));
+ });
+
+ test("POST /api/gbv/submit rejects nonce mismatch", async () => {
+ const init = await postJson(initRoute, "/api/gbv/init", {
+ artifact: demoArtifact,
+ });
+
+ const initJson = init.json as InitResponse;
+ const pages = buildPages(initJson, { gradePercent: 100, moduleCount: 5 });
+ pages[0] = { ...pages[0], injectedNonce: "wrong-nonce" };
+
+ const submit = await postJson(submitRoute, "/api/gbv/submit", {
+ sessionId: initJson.sessionId,
+ courseId: demoArtifact.courseId,
+ provider: "synthetic",
+ pages,
+ });
+
+ assert.equal(submit.status, 400);
+ assert.equal(submit.json.ok, false);
+ assert.equal(submit.json.code, "INJECTED_NONCE_MISMATCH");
+ });
+
+ test("POST /api/gbv/submit rejects expired session", async () => {
+ const init = await postJson(initRoute, "/api/gbv/init", {
+ artifact: demoArtifact,
+ });
+
+ const initJson = init.json as InitResponse;
+ expireSessionNow(initJson.sessionId);
+
+ const submit = await postJson(submitRoute, "/api/gbv/submit", {
+ sessionId: initJson.sessionId,
+ courseId: demoArtifact.courseId,
+ provider: "synthetic",
+ pages: buildPages(initJson, { gradePercent: 100, moduleCount: 5 }),
+ });
+
+ assert.equal(submit.status, 403);
+ assert.equal(submit.json.ok, false);
+ assert.equal(submit.json.code, "SESSION_EXPIRED");
+ });
+});
diff --git a/apps/server/tests/attack-mixed-grade.test.ts b/apps/server/tests/attack-mixed-grade.test.ts
new file mode 100644
index 0000000..d2a1700
--- /dev/null
+++ b/apps/server/tests/attack-mixed-grade.test.ts
@@ -0,0 +1,181 @@
+import assert from "node:assert/strict";
+import { beforeEach, describe, test } from "node:test";
+import { buildSurfaceUrlsFromPlan, type GbvPageType, type GbvVerificationArtifact } from "@gbv/core";
+import { gbvConfig } from "@gbv/config";
+import { POST as initRoute } from "../src/app/api/gbv/init/route";
+import { POST as submitRoute } from "../src/app/api/gbv/submit/route";
+import { resetMemoryStore } from "../src/lib/store/memoryStore";
+
+interface InitResponse {
+ ok: boolean;
+ sessionId: string;
+ artifact: GbvVerificationArtifact;
+ pagePlan: string[];
+ pageNonces: string[];
+}
+
+const demoArtifact: GbvVerificationArtifact = {
+ courseId: 101,
+ publicCourseKey: "csk_7r2q9p",
+ certificateId: "O04CRAKZMLZJ",
+ title: "Getting Started with Microsoft Excel",
+};
+
+async function postJson(
+ handler: (req: Request) => Promise,
+ path: string,
+ body: unknown,
+): Promise<{ status: number; json: any }> {
+ const req = new Request(`http://localhost${path}`, {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify(body),
+ });
+
+ const res = await handler(req);
+ return {
+ status: res.status,
+ json: await res.json(),
+ };
+}
+
+function createSemanticHtml(params: {
+ pageType: string;
+ nonce: string;
+ url: string;
+ gradePercent: number;
+ moduleCount: number;
+ courseName: string;
+ courseKey: string;
+ courseSlug: string;
+ certificateId: string;
+ courseId: number;
+}): string {
+ return `
+
+
+
+
+ ${params.pageType}
+
+
+
+
+ ${params.pageType}
+ Grade Achieved: ${params.gradePercent}%
+ URL: ${params.url}
+
+`;
+}
+
+function buildPages(init: InitResponse, gradePercent: number, moduleCount = 5) {
+ const urls = buildSurfaceUrlsFromPlan({
+ origin: gbvConfig.origins.syntheticClient,
+ pagePlan: init.pagePlan as GbvPageType[],
+ templates: gbvConfig.demo.surfacePathTemplates,
+ artifact: init.artifact,
+ });
+
+ return urls.map((url, index) => ({
+ url,
+ injectedNonce: init.pageNonces[index],
+ html: createSemanticHtml({
+ pageType: init.pagePlan[index],
+ nonce: init.pageNonces[index],
+ url,
+ gradePercent,
+ moduleCount,
+ courseName: init.artifact.title || "Synthetic Course",
+ courseKey: init.artifact.publicCourseKey,
+ courseSlug: "synthetic-slug",
+ certificateId: init.artifact.certificateId,
+ courseId: init.artifact.courseId,
+ }),
+ }));
+}
+
+describe("GBV attack regression", () => {
+ beforeEach(() => {
+ resetMemoryStore();
+ });
+
+ test("rejects mixed-grade tampering attack with multiple mutated leaves", async () => {
+ const init = await postJson(initRoute, "/api/gbv/init", {
+ artifact: demoArtifact,
+ });
+ const initJson = init.json as InitResponse;
+ const pages = buildPages(initJson, 100, 5);
+
+ const assignmentsIndex = initJson.pagePlan.indexOf("assignments");
+ const proofIndex = initJson.pagePlan.indexOf("proof");
+ assert.notEqual(assignmentsIndex, -1);
+ assert.notEqual(proofIndex, -1);
+
+ pages[assignmentsIndex] = {
+ ...pages[assignmentsIndex],
+ html: createSemanticHtml({
+ pageType: "assignments",
+ nonce: initJson.pageNonces[assignmentsIndex],
+ url: pages[assignmentsIndex].url,
+ gradePercent: 50,
+ moduleCount: 9,
+ courseName: initJson.artifact.title || "Synthetic Course",
+ courseKey: `${initJson.artifact.publicCourseKey}-tampered`,
+ courseSlug: "synthetic-slug",
+ certificateId: initJson.artifact.certificateId,
+ courseId: initJson.artifact.courseId,
+ }),
+ };
+
+ pages[proofIndex] = {
+ ...pages[proofIndex],
+ html: createSemanticHtml({
+ pageType: "proof",
+ nonce: initJson.pageNonces[proofIndex],
+ url: pages[proofIndex].url,
+ gradePercent: 100,
+ moduleCount: 5,
+ courseName: initJson.artifact.title || "Synthetic Course",
+ courseKey: initJson.artifact.publicCourseKey,
+ courseSlug: "synthetic-slug",
+ certificateId: `${initJson.artifact.certificateId}-ALT`,
+ courseId: initJson.artifact.courseId,
+ }),
+ };
+
+ const submit = await postJson(submitRoute, "/api/gbv/submit", {
+ sessionId: initJson.sessionId,
+ courseId: initJson.artifact.courseId,
+ provider: "synthetic",
+ pages,
+ });
+
+ assert.equal(submit.status, 400);
+ assert.equal(submit.json.ok, false);
+ assert.equal(submit.json.code, "SEMANTIC_VERIFICATION_FAILED");
+ assert.ok(Array.isArray(submit.json.meta.failedInvariantIds));
+
+ const invariants = submit.json.meta.verificationLog.semantic.invariants as Array<{
+ id: string;
+ status: "pass" | "fail" | "warn";
+ }>;
+ const failedIds = invariants
+ .filter((invariant) => invariant.status === "fail")
+ .map((invariant) => invariant.id);
+
+ assert.ok(failedIds.includes("grade_threshold"));
+ assert.ok(failedIds.includes("module_count_consistency"));
+ assert.ok(failedIds.includes("course_key_consistency"));
+ assert.ok(failedIds.includes("certificate_id_consistency"));
+ assert.ok(submit.json.meta.failedInvariantIds.includes("grade_threshold"));
+ assert.ok(submit.json.meta.failedInvariantIds.includes("course_key_consistency"));
+ });
+});
diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json
new file mode 100644
index 0000000..373adcc
--- /dev/null
+++ b/apps/server/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "jsx": "preserve",
+ "baseUrl": ".",
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "scripts/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..630ccda
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,55 @@
+# GBV Documentation
+
+> Documentation version: **v0.214**
+> Corresponds to the February 14, 2026 GBV development snapshot.
+
+This directory contains technical documentation for the
+**Glass Ballroom Verification (GBV)** reference implementation.
+
+The documents below are organized by purpose to help readers
+navigate the protocol, security model, and reference environment.
+
+---
+
+## Start Here
+
+- [Protocol Overview](./protocol-overview.md)
+ Conceptual explanation of GBV stages, verification flow, and outcome model.
+
+- [Architecture](./architecture.md)
+ System structure and runtime interaction between extension, client surfaces, and verifier.
+
+---
+
+## Security Model
+
+- [Threat Model](./threat-model.md)
+ Adversarial assumptions and protocol security objectives.
+
+- [Traceability](./traceability.md)
+ Mapping between protocol stages and implementation artifacts.
+
+---
+
+## Running the Reference Implementation
+
+- [Demo Walkthrough](./demo-walkthrough.md)
+ Step-by-step manual verification flow and expected outcomes.
+
+---
+
+## Development and Design Notes
+
+- [Development Notes](./development-notes.md)
+ Implementation alignment rules and active design constraints.
+
+- [Release Hardening Report](./release-hardening-report.md)
+ Stabilization work and guarantees introduced for the v0.214 release.
+
+---
+
+## Research
+
+- [GBV Public Research Draft (v0.214)](./research/gbv_public_v0.214.pdf)
+
+This draft marks the public transition from ARGON-V to GBV.
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..7b0e2eb
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,24 @@
+# Architecture
+
+The GBV reference implementation is organized as a workspace monorepo with clear separation of responsibilities:
+
+- `apps/server`: verifier APIs and session lifecycle.
+- `apps/client/synthetic`: synthetic multi-surface platform used for reproducible verification runs.
+- `apps/extension`: MV3 client that performs browser-observable collection.
+- `packages/gbv-core`: canonicalization, invariant checks, commitments, and receipt primitives.
+- `packages/gbv-config`: runtime configuration loader/validation shared across apps.
+
+## Runtime Flow
+
+1. The extension popup selects a course artifact and starts a run.
+2. The background service worker requests a challenge from `POST /api/gbv/init`.
+3. The extension traverses configured surfaces and captures browser-observable snapshots.
+4. Captured pages are submitted to `POST /api/gbv/submit`.
+5. The server validates structure and semantic consistency, then returns receipt/metadata.
+
+## Design Constraints
+
+- Server decisions are independent of dataset validity labels.
+- Client display is transparent: server metadata is visible to users and developers.
+- Shared protocol behavior lives in `@gbv/core` to avoid drift.
+- Configuration is centralized in `gbv.config.ts`.
diff --git a/docs/demo-walkthrough.md b/docs/demo-walkthrough.md
new file mode 100644
index 0000000..8194095
--- /dev/null
+++ b/docs/demo-walkthrough.md
@@ -0,0 +1,30 @@
+# Demo Walkthrough
+
+## Prerequisites
+
+```bash
+corepack prepare pnpm@10.4.1 --activate
+corepack pnpm bootstrap
+corepack pnpm dev
+```
+
+## Manual Verification Flow
+
+1. Load `apps/extension/build` as an unpacked extension in Chrome (`chrome://extensions`).
+2. Open `http://localhost:3000/hub`.
+3. Open the extension popup.
+4. Choose a course in the dropdown.
+5. Click **Verify**.
+
+## What You Should See
+
+- A structured summary (state, status, score, decision code, identifiers, timing).
+- Grouped protocol sections (session/challenge, commitments, semantic findings).
+- Expandable **Raw Response** containing the verbatim server payload.
+
+## Expected Baseline Outcomes
+
+- `csk_7r2q9p`: accepted.
+- `csk_0a81lm`: semantic verification failure (`module_count_consistency`).
+- `csk_3z19tt`: semantic verification failure (`certificate_id_consistency`).
+- `csk_t1mix`: semantic verification failure (`grade_threshold`, `course_key_consistency`).
diff --git a/docs/development-notes.md b/docs/development-notes.md
new file mode 100644
index 0000000..b7462a5
--- /dev/null
+++ b/docs/development-notes.md
@@ -0,0 +1,15 @@
+# Development Notes
+
+This document tracks implementation-level notes that are useful during active GBV development.
+
+## Primary References
+
+- Architecture: [`docs/architecture.md`](./architecture.md)
+- Protocol behavior: [`docs/protocol-overview.md`](./protocol-overview.md)
+- Threat assumptions: [`docs/threat-model.md`](./threat-model.md)
+
+## Current Focus Areas
+
+- Keep verifier-side invariants centralized in [`packages/gbv-core`](../packages/gbv-core/).
+- Keep runtime config authoritative in [`gbv.config.ts`](../gbv.config.ts).
+- Keep extension behavior aligned with server contract and documented in [`docs/demo-walkthrough.md`](./demo-walkthrough.md).
diff --git a/docs/protocol-overview.md b/docs/protocol-overview.md
new file mode 100644
index 0000000..2e7e5cf
--- /dev/null
+++ b/docs/protocol-overview.md
@@ -0,0 +1,24 @@
+# Protocol Overview
+
+GBV evaluates whether independently observed surfaces can represent one coherent state.
+
+## Stage Mapping
+
+1. Stage I: challenge issuance (`/api/gbv/init`).
+2. Stage II: multi-surface collection in a normal browser context.
+3. Stage III: nonce/structure and semantic invariant verification.
+4. Stage IV: deterministic canonicalization of observed data.
+5. Stage V: commitment and signed receipt generation.
+
+## Key Properties
+
+- Verification is server-authoritative.
+- Decisions derive from observed evidence and protocol checks.
+- No privileged provider APIs are required.
+- The extension can display full server protocol metadata without violating blindness.
+
+## Outcome Model
+
+- `ok: true` + `accepted: true` indicates coherent evidence.
+- `ok: false` with structured error code indicates invariant/protocol failure.
+- Raw response payload remains available for exact inspection.
diff --git a/docs/release-hardening-report.md b/docs/release-hardening-report.md
new file mode 100644
index 0000000..ec751be
--- /dev/null
+++ b/docs/release-hardening-report.md
@@ -0,0 +1,63 @@
+# Release Hardening Report
+
+## What Was Broken and How It Was Fixed
+
+- Extension output was raw-JSON-first and hard to inspect.
+ - Fixed by implementing structured summary sections plus grouped protocol details.
+- Demo verification could report pass without strict semantic expectations.
+ - Fixed by making `demo:verify` assert exact baseline/adversarial outcomes and mismatch categories.
+- No deterministic extension runtime smoke check existed.
+ - Fixed by adding Playwright extension smoke script and CI job (service worker + messaging).
+- Protocol observability lacked explicit request/timing metadata.
+ - Fixed by adding server request IDs, stage timing summaries, and trace-safe response metadata.
+- Deep documentation was not organized under `/docs`.
+ - Fixed by creating `/docs` and moving long-form technical content there.
+
+## End-to-End Verification Performed
+
+Commands run:
+
+```bash
+corepack pnpm bootstrap
+corepack pnpm lint
+corepack pnpm typecheck
+corepack pnpm test
+corepack pnpm demo:verify
+```
+
+Expected/observed outcomes:
+
+- lint/typecheck: pass
+- tests: pass (`@gbv/core` unit + server integration suites)
+- demo verify: strict pass with:
+ - `csk_7r2q9p: ACCEPTED (strict checks passed)`
+ - `csk_t1mix: REJECTED (strict checks passed)`
+ - `OVERALL: PASS (strict assertions)`
+
+Extension smoke command:
+
+```bash
+corepack pnpm test:extension-smoke
+```
+
+Local note: requires Playwright Chromium install first (`corepack pnpm exec playwright install chromium`). CI installs this automatically.
+
+## New/Updated Tests and Guarantees
+
+- `packages/gbv-core/tests/receipt.test.ts`
+ - guarantees tampered receipt signatures fail validation.
+- `packages/gbv-core/tests/merkle.test.ts`
+ - guarantees invalid merkle roots fail proof validation.
+- `packages/gbv-core/tests/invariants.test.ts`
+ - guarantees structure-bound nonce and page-plan invariant failures are detected.
+- `apps/server/tests/api-contract.test.ts`
+ - guarantees pass-path receipts are cryptographically verifiable.
+- `apps/server/tests/attack-mixed-grade.test.ts`
+ - guarantees fail-path exposes expected mismatch categories.
+- `scripts/extension-smoke.ts`
+ - guarantees MV3 background service worker starts and handles `GBV_PING`.
+
+## Remaining Known Issues
+
+1. Playwright browser binaries are not bundled by default and must be installed once locally before running extension smoke tests.
+2. Server info logs remain enabled in non-debug mode (request/timing diagnostics) by design; only verbose debug traces are gated by `GBV_DEBUG`.
diff --git a/docs/research/gbv_public_v0.214.pdf b/docs/research/gbv_public_v0.214.pdf
new file mode 100644
index 0000000..73070f1
Binary files /dev/null and b/docs/research/gbv_public_v0.214.pdf differ
diff --git a/docs/theory/README.md b/docs/theory/README.md
deleted file mode 100644
index 85ed405..0000000
--- a/docs/theory/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# ARGON-V Research Paper
-
-This directory contains analytical and theoretical materials related to the
-ARGON-V protocol.
-
-`research-paper-v0.1.pdf` is a **v0.1 protocol and systems design draft** describing
-the ARGON-V threat model, protocol stages, and security intuition.
-
-The paper is **not an empirical security evaluation** and does not report
-measured attack success rates or performance benchmarks. Quantitative models
-and probability expressions are illustrative and analytical.
-
-A working reference implementation exists but is not yet open-sourced.
-Formal proofs and empirical validation are planned future work.
diff --git a/docs/theory/research-paper-v0.1.pdf b/docs/theory/research-paper-v0.1.pdf
deleted file mode 100644
index 83417a0..0000000
Binary files a/docs/theory/research-paper-v0.1.pdf and /dev/null differ
diff --git a/docs/threat-model.md b/docs/threat-model.md
new file mode 100644
index 0000000..85bb98f
--- /dev/null
+++ b/docs/threat-model.md
@@ -0,0 +1,22 @@
+# Threat Model
+
+GBV is designed for adversarial surface manipulation scenarios in which:
+
+- attackers can alter one or more client-observable surfaces,
+- attackers can mix surfaces from different legitimate states,
+- verifier logic must operate without privileged provider-side ground truth.
+
+## In Scope
+
+- nonce binding and session misuse attempts,
+- cross-surface semantic inconsistencies,
+- evidence tampering that breaks deterministic commitments.
+
+## Out of Scope
+
+- hosted production hardening concerns outside protocol semantics (for example auth/rate limiting),
+- provider-controlled trust anchors not present in this local reference environment.
+
+## Security Objective
+
+Detect when submitted observations cannot simultaneously satisfy configured invariants for a single coherent state.
diff --git a/docs/threat-model/adversarial-analysis.md b/docs/threat-model/adversarial-analysis.md
deleted file mode 100644
index 5f6677c..0000000
--- a/docs/threat-model/adversarial-analysis.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Adversarial Analysis
-
-## 1. Adversary Definition
-The ARGON-V protocol assumes a **Sophisticated Rational Adversary** ($\mathcal{A}$). $\mathcal{A}$ has complete control over the client-side execution environment (the Prover) and is motivated by the utility $U$ of a successful forgery.
-
-### 1.1 Adversarial Capabilities
-* **Runtime Manipulation**: $\mathcal{A}$ can pause, modify, or replay JavaScript execution.
-* **DOM Injection**: $\mathcal{A}$ can inject or hide elements within the Source ($\mathcal{S}$) to mimic a valid state.
-* **Network Interception**: $\mathcal{A}$ can spoof API responses from $\mathcal{S}$ before they reach the observation agent.
-
-## 2. The Economic Security Model
-The ARGON-V protocol operates on the principle of **Deterministic Adversarial Friction**. This refers to the predictable increase in coordination cost imposed on an adversary as the number of required coherent observations $k$ grows. While the protocol acknowledges the inherent mutability of client-side runtimes, it enforces a security threshold where the cost of a coordinated forgery is mathematically decoupled from the ease of local manipulation.
-
-Security is maintained via **Economic Exhaustion**:
-
-$$C(\text{forgery}) > U(\text{forgery})$$
-
-> **Security Invariant**: The cost ($C$) required to maintain a logically consistent state across $k$ surfaces must strictly exceed the utility ($U$) derived from the forged attestation.
-
-By requiring $k$ independent surfaces to maintain semantic coherence under a server-issued challenge $\eta$, the protocol transforms a low-cost data injection attack into a high-complexity coordination problem. Independent surfaces are defined as observations that cannot be trivially derived from a single DOM mutation or API interception without maintaining cross-surface consistency.
-
-## 3. Attack Vectors & Mitigations
-
-### 3.1 The "Single Surface" Spoof
-**Attack**: $\mathcal{A}$ modifies the HTML of a single page to show a false balance or status.
-**Mitigation**: **Multi-Surface Coherence ($k > 1$)**. The protocol requires $k$ independent surfaces. $\mathcal{A}$ must now coordinate the forgery across multiple URLs or UI states, exponentially increasing the complexity of the script required to maintain the lie.
-
-### 3.2 Temporal Replay
-**Attack**: $\mathcal{A}$ records a valid state from the past and attempts to re-submit it.
-**Mitigation**: **Ephemeral Nonce ($\eta$)**. The observation is cryptographically bound to a unique, time-sensitive challenge $\eta$. A captured state from $T_{-1}$ cannot satisfy the challenge for $T_{0}$ within the window $\Delta t$.
-
-### 3.3 Semantic Dissonance
-**Attack**: $\mathcal{A}$ fakes data that looks structurally correct but is logically impossible.
-**Mitigation**: **Constraint Set ($\mathcal{C}$)**. The Verifier enforces platform-specific invariants (e.g., "The sum of the ledger must equal the displayed balance").
-
-## 4. Residual Risk & Scope
-The protocol remains vulnerable to **Platform-Level Corruption**, where the Source $\mathcal{S}$ itself provides false data. ARGON-V makes no claims about detecting misinformation originating from a compromised or malicious platform; it verifies observation fidelity, not platform honesty. The protocol is designed to verify that "The Platform said X," not necessarily that "X is the ultimate truth of the universe."
diff --git a/docs/traceability.md b/docs/traceability.md
new file mode 100644
index 0000000..0ff1b66
--- /dev/null
+++ b/docs/traceability.md
@@ -0,0 +1,17 @@
+# Traceability
+
+This document maps protocol stages to implementation artifacts.
+
+## Stage to Code Mapping
+
+- Stage I (challenge issuance): [`apps/server/src/app/api/gbv/init/route.ts`](../apps/server/src/app/api/gbv/init/route.ts)
+- Stage II (surface collection): [`apps/extension/src`](../apps/extension/src/)
+- Stage III (verification): [`packages/gbv-core/src/semantic`](../packages/gbv-core/src/semantic/)
+- Stage IV (canonicalization): [`packages/gbv-core/src/providers/synthetic`](../packages/gbv-core/src/providers/synthetic/)
+- Stage V (commitment + receipt): [`apps/server/src/lib/gbvServerService.ts`](../apps/server/src/lib/gbvServerService.ts), [`packages/gbv-core/src/receipt.ts`](../packages/gbv-core/src/receipt.ts)
+
+## Contract and Validation References
+
+- API contract tests: [`apps/server/tests/api-contract.test.ts`](../apps/server/tests/api-contract.test.ts)
+- Attack regression tests: [`apps/server/tests/attack-mixed-grade.test.ts`](../apps/server/tests/attack-mixed-grade.test.ts)
+- Demo strict verifier flow: [`scripts/demo-verify.ts`](../scripts/demo-verify.ts)
diff --git a/gbv.config.ts b/gbv.config.ts
new file mode 100644
index 0000000..7df3c8e
--- /dev/null
+++ b/gbv.config.ts
@@ -0,0 +1,63 @@
+const debugEnabled =
+ typeof process !== "undefined" &&
+ (process.env.GBV_DEBUG === "1" || process.env.GBV_DEBUG === "true");
+
+const gbvConfig = {
+ versions: {
+ protocol: "0.71",
+ implementation: "1.0.0",
+ },
+ environment: {
+ nodeMajor: 22,
+ mode: "development",
+ debug: debugEnabled,
+ },
+ ports: {
+ server: 3001,
+ syntheticClient: 3000,
+ },
+ origins: {
+ server: "http://localhost:3001",
+ syntheticClient: "http://localhost:3000",
+ },
+ protocol: {
+ providerId: "synthetic",
+ nonceLeafId: "data-gbv-nonce",
+ sessionTtlMs: 60_000,
+ maxPages: 10,
+ maxHtmlChars: 2_000_000,
+ minGradePercent: 60,
+ receiptHmacSecret: "gbv-local-dev-secret",
+ },
+ demo: {
+ pagePlan: [
+ "hub",
+ "course",
+ "assignments",
+ "proof",
+ "certificate",
+ "milestones",
+ ],
+ surfacePathTemplates: {
+ hub: "/hub",
+ course: "/c/:publicCourseKey",
+ assignments: "/c/:publicCourseKey/assignments",
+ proof: "/proof/:certificateId",
+ certificate: "/certificate/:certificateId",
+ milestones: "/milestones",
+ },
+ courseCatalogApiPath: "/api/gbv/courses",
+ verifierBaselineCourseKey: "csk_7r2q9p",
+ verifierAdversarialCourseKey: "csk_t1mix",
+ },
+ extension: {
+ name: "GBV Client",
+ version: "1.0.0",
+ buildDir: "apps/extension/build",
+ hostPermissions: ["http://localhost:3000/*", "http://localhost:3001/*"],
+ contentScriptMatches: ["http://localhost:3000/*"],
+ permissions: ["storage", "tabs", "scripting"],
+ },
+} as const;
+
+export default gbvConfig;
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7516c8a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "gbv-reference-implementation",
+ "private": true,
+ "version": "1.0.0",
+ "description": "Official OSS reference implementation for Glass Ballroom Verification (GBV).",
+ "packageManager": "pnpm@10.4.1",
+ "engines": {
+ "node": ">=22.0.0"
+ },
+ "scripts": {
+ "bootstrap": "corepack pnpm install && corepack pnpm --filter @gbv/extension build",
+ "dev": "tsx scripts/dev-orchestrator.ts",
+ "build": "corepack pnpm -r --if-present run build",
+ "build:extension": "corepack pnpm --filter @gbv/extension build",
+ "lint": "corepack pnpm -r --if-present run lint",
+ "format": "prettier --write .",
+ "typecheck": "corepack pnpm -r --if-present run typecheck",
+ "test": "corepack pnpm -r --if-present run test",
+ "test:server": "corepack pnpm --filter @gbv/server test",
+ "test:attack": "corepack pnpm --filter @gbv/server exec tsx --test tests/attack-mixed-grade.test.ts",
+ "test:extension-smoke": "tsx scripts/extension-smoke.ts",
+ "demo:verify": "tsx scripts/demo-verify.ts",
+ "clean": "rimraf --glob \"apps/**/.next\" \"apps/**/build\" \"apps/**/dist\" \"packages/**/dist\""
+ },
+ "devDependencies": {
+ "@types/node": "^22.13.4",
+ "chrome-launcher": "^1.2.1",
+ "playwright": "^1.51.1",
+ "prettier": "^3.5.2",
+ "puppeteer-core": "^24.26.1",
+ "rimraf": "^6.0.1",
+ "tsx": "^4.19.3",
+ "typescript": "^5.7.3"
+ }
+}
diff --git a/packages/core/README.md b/packages/core/README.md
deleted file mode 100644
index 3b616f9..0000000
--- a/packages/core/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Core
diff --git a/packages/extension/README.md b/packages/extension/README.md
deleted file mode 100644
index 9257c88..0000000
--- a/packages/extension/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Extension
diff --git a/packages/gbv-config/package.json b/packages/gbv-config/package.json
new file mode 100644
index 0000000..b9e10ea
--- /dev/null
+++ b/packages/gbv-config/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@gbv/config",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "exports": {
+ ".": "./src/index.ts"
+ },
+ "scripts": {
+ "typecheck": "tsc -p tsconfig.json"
+ }
+}
\ No newline at end of file
diff --git a/packages/gbv-config/src/index.ts b/packages/gbv-config/src/index.ts
new file mode 100644
index 0000000..e5634b1
--- /dev/null
+++ b/packages/gbv-config/src/index.ts
@@ -0,0 +1,52 @@
+import rawConfig from "../../../gbv.config";
+
+/**
+ * Runtime-validated GBV configuration.
+ *
+ * This module is the single source of truth consumed by all workspace apps.
+ */
+export const gbvConfig = (() => {
+ const config = (rawConfig as unknown as { default?: typeof rawConfig }).default ?? rawConfig;
+
+ if (!config.protocol?.providerId) {
+ throw new Error("GBV config invalid: missing protocol.providerId");
+ }
+ if (!config.protocol?.nonceLeafId) {
+ throw new Error("GBV config invalid: missing protocol.nonceLeafId");
+ }
+ if (!Number.isFinite(config.protocol?.sessionTtlMs)) {
+ throw new Error("GBV config invalid: protocol.sessionTtlMs must be numeric");
+ }
+ if (!Array.isArray(config.demo?.pagePlan) || config.demo.pagePlan.length < 1) {
+ throw new Error("GBV config invalid: demo.pagePlan must be non-empty");
+ }
+ if (!config.demo?.surfacePathTemplates) {
+ throw new Error("GBV config invalid: demo.surfacePathTemplates must be provided");
+ }
+ if (!config.demo?.courseCatalogApiPath) {
+ throw new Error("GBV config invalid: demo.courseCatalogApiPath must be provided");
+ }
+ if (!Array.isArray(config.extension?.hostPermissions) || config.extension.hostPermissions.length < 1) {
+ throw new Error("GBV config invalid: extension.hostPermissions must be non-empty");
+ }
+ if (
+ !Array.isArray(config.extension?.contentScriptMatches) ||
+ config.extension.contentScriptMatches.length < 1
+ ) {
+ throw new Error("GBV config invalid: extension.contentScriptMatches must be non-empty");
+ }
+
+ return config;
+})();
+
+export type GbvConfig = typeof gbvConfig;
+
+/**
+ * Build canonical localhost origin from configured port.
+ *
+ * @param {number} port Port number.
+ * @returns {string} Origin URL.
+ */
+export function localOrigin(port: number): string {
+ return `http://localhost:${port}`;
+}
diff --git a/packages/gbv-config/tsconfig.json b/packages/gbv-config/tsconfig.json
new file mode 100644
index 0000000..d8cbe8b
--- /dev/null
+++ b/packages/gbv-config/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "composite": true
+ },
+ "include": ["src/**/*.ts", "../../gbv.config.ts"]
+}
\ No newline at end of file
diff --git a/packages/gbv-core/package.json b/packages/gbv-core/package.json
new file mode 100644
index 0000000..f5ed14d
--- /dev/null
+++ b/packages/gbv-core/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@gbv/core",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "exports": {
+ ".": "./src/index.ts",
+ "./browser": "./src/browser.ts"
+ },
+ "dependencies": {
+ "cheerio": "^1.1.2"
+ },
+ "scripts": {
+ "typecheck": "tsc -p tsconfig.json",
+ "test": "tsx --test tests/**/*.test.ts"
+ }
+}
diff --git a/packages/gbv-core/src/browser.ts b/packages/gbv-core/src/browser.ts
new file mode 100644
index 0000000..b27365f
--- /dev/null
+++ b/packages/gbv-core/src/browser.ts
@@ -0,0 +1,4 @@
+export { syntheticProvider } from "./providers/synthetic";
+export type { GbvPageType } from "./types";
+export { buildSurfaceUrlsFromPlan } from "./surfacePlan";
+export type { GbvVerificationArtifact } from "./types";
diff --git a/packages/gbv-core/src/index.ts b/packages/gbv-core/src/index.ts
new file mode 100644
index 0000000..21cc847
--- /dev/null
+++ b/packages/gbv-core/src/index.ts
@@ -0,0 +1,7 @@
+export * from "./types";
+export * from "./merkle";
+export * from "./receipt";
+export * from "./surfacePlan";
+export * from "./semantic/verifyProviderConsistency";
+export * from "./semantic/verifyStructureBoundNonces";
+export * from "./providers/synthetic";
diff --git a/packages/gbv-core/src/merkle.ts b/packages/gbv-core/src/merkle.ts
new file mode 100644
index 0000000..97869fb
--- /dev/null
+++ b/packages/gbv-core/src/merkle.ts
@@ -0,0 +1,91 @@
+import crypto from "node:crypto";
+
+/**
+ * Spec §Stage V: hash canonical evidence leaves with SHA-256.
+ *
+ * @param {string | Buffer} data Leaf or node payload.
+ * @returns {string} Lowercase hex SHA-256 digest.
+ */
+export function sha256Hex(data: string | Buffer): string {
+ const payload = typeof data === "string" ? Buffer.from(data, "utf8") : data;
+ return crypto.createHash("sha256").update(payload).digest("hex").toLowerCase();
+}
+
+/**
+ * Spec §Stage V: deterministic Merkle root over ordered leaf hashes.
+ *
+ * @param {string[]} leafHashes Ordered leaf hashes.
+ * @returns {string} Root hash in hex format.
+ */
+export function buildMerkleRoot(leafHashes: string[]): string {
+ if (!leafHashes.length) return "";
+
+ let level: Uint8Array[] = leafHashes.map((hash) => Buffer.from(hash, "hex"));
+
+ while (level.length > 1) {
+ const next: Uint8Array[] = [];
+
+ for (let i = 0; i < level.length; i += 2) {
+ const left = level[i];
+ const right = level[i + 1] ?? left;
+ const combined = Buffer.concat([left, right]);
+ next.push(crypto.createHash("sha256").update(combined).digest());
+ }
+
+ level = next;
+ }
+
+ return Buffer.from(level[0]).toString("hex");
+}
+
+/**
+ * Verify a Merkle proof against a known root hash.
+ *
+ * @param {object} params Proof data.
+ * @param {string} params.leaf Leaf value.
+ * @param {number} params.index Leaf index.
+ * @param {string[]} params.proof Sibling path.
+ * @param {string} params.expectedRoot Expected Merkle root.
+ * @param {boolean} [params.isHashed=false] Whether leaf is already hashed.
+ * @returns {boolean} True when proof is valid.
+ */
+export function verifyMerkleProof({
+ leaf,
+ index,
+ proof,
+ expectedRoot,
+ isHashed = false,
+}: {
+ leaf: string;
+ index: number;
+ proof: string[];
+ expectedRoot: string;
+ isHashed?: boolean;
+}): boolean {
+ if (!leaf || !Number.isInteger(index) || !Array.isArray(proof)) return false;
+
+ const normalizedRoot = String(expectedRoot || "").toLowerCase();
+ if (!/^[0-9a-f]{64}$/.test(normalizedRoot)) return false;
+
+ const leafHash = isHashed ? String(leaf).toLowerCase() : sha256Hex(leaf);
+ if (!/^[0-9a-f]{64}$/.test(leafHash)) return false;
+
+ let cursor = Buffer.from(leafHash, "hex");
+ let cursorIndex = index;
+
+ for (const siblingHexRaw of proof) {
+ const siblingHex = String(siblingHexRaw || "").toLowerCase();
+ if (!/^[0-9a-f]{64}$/.test(siblingHex)) return false;
+
+ const sibling = Buffer.from(siblingHex, "hex");
+ const isRight = cursorIndex % 2 === 1;
+ const combined = isRight
+ ? Buffer.concat([sibling, cursor])
+ : Buffer.concat([cursor, sibling]);
+
+ cursor = crypto.createHash("sha256").update(combined).digest();
+ cursorIndex = Math.floor(cursorIndex / 2);
+ }
+
+ return cursor.toString("hex") === normalizedRoot;
+}
diff --git a/packages/gbv-core/src/providers/synthetic.ts b/packages/gbv-core/src/providers/synthetic.ts
new file mode 100644
index 0000000..7b886af
--- /dev/null
+++ b/packages/gbv-core/src/providers/synthetic.ts
@@ -0,0 +1,111 @@
+import * as cheerio from "cheerio";
+import type { GbvPageType } from "../types";
+
+function normalize(input: string): string {
+ return String(input || "")
+ .trim()
+ .replace(/\s+/g, " ")
+ .toLowerCase();
+}
+
+function parsePercent(input: string): number | null {
+ const match = String(input || "").match(/(\d+(?:\.\d+)?)\s*%/);
+ if (!match) return null;
+ const value = Number.parseFloat(match[1]);
+ return Number.isFinite(value) ? value : null;
+}
+
+function pathOf(url: string): string {
+ try {
+ const parsed = new URL(url);
+ return `${parsed.origin}${parsed.pathname}`;
+ } catch {
+ return String(url || "").split("?")[0].split("#")[0];
+ }
+}
+
+/** Synthetic provider implementation for GBV OSS demo. */
+export const syntheticProvider = {
+ id: "synthetic",
+
+ detectPageType(url: string): GbvPageType {
+ const normalized = String(url || "");
+ if (normalized.includes("/hub")) return "hub";
+ if (/\/c\/[^/]+\/assignments/.test(normalized)) return "assignments";
+ if (/\/c\/[^/]+$/.test(normalized) || normalized.includes("/c/")) return "course";
+ if (normalized.includes("/proof/")) return "proof";
+ if (normalized.includes("/certificate/")) return "certificate";
+ if (normalized.includes("/milestones")) return "milestones";
+ return "unknown";
+ },
+
+ canonicalize({
+ html,
+ url,
+ nonceLeafId,
+ }: {
+ html: string;
+ url: string;
+ nonceLeafId: string;
+ }): string[] {
+ const $ = cheerio.load(String(html || ""));
+ const pageType = this.detectPageType(url);
+ const leaves: string[] = [];
+
+ leaves.push("provider:synthetic");
+ leaves.push(`page:type:${pageType}`);
+ leaves.push(`page:path:${pathOf(url)}`);
+
+ const semanticNodes = $("[data-gbv-semantic='true']");
+ semanticNodes.each((_, node) => {
+ const element = $(node);
+ const courseName = normalize(element.attr("data-gbv-course-name") || "");
+ const courseId = String(element.attr("data-gbv-course-id") || "").trim();
+ const courseKey = String(element.attr("data-gbv-course-key") || "").trim();
+ const courseSlug = String(element.attr("data-gbv-course-slug") || "").trim();
+ const certificateId = String(element.attr("data-gbv-certificate-id") || "").trim();
+ const gradePercent = Number.parseFloat(
+ String(element.attr("data-gbv-grade-percent") || "NaN"),
+ );
+ const progressPercent = Number.parseFloat(
+ String(element.attr("data-gbv-progress-percent") || "NaN"),
+ );
+ const moduleCount = Number.parseFloat(
+ String(element.attr("data-gbv-module-count") || "NaN"),
+ );
+
+ if (courseName) leaves.push(`course:name:${courseName}`);
+ if (courseId) leaves.push(`course:id:${courseId}`);
+ if (courseKey) leaves.push(`course:key:${courseKey}`);
+ if (courseSlug) leaves.push(`course:slug:${courseSlug}`);
+ if (certificateId) leaves.push(`certificate:id:${certificateId}`);
+ if (Number.isFinite(gradePercent)) leaves.push(`course:grade:${gradePercent}%`);
+ if (Number.isFinite(progressPercent)) {
+ leaves.push(`course:progress:${progressPercent}%`);
+ }
+ if (Number.isFinite(moduleCount)) {
+ leaves.push(`course:module-count:${moduleCount}`);
+ }
+ });
+
+ // Fallback extraction is restricted to artifact-specific pages. Listing pages
+ // (hub/milestones) intentionally omit semantic tags and must stay neutral.
+ if (!semanticNodes.length && !["hub", "milestones"].includes(pageType)) {
+ const title = normalize($("h1").first().text() || "");
+ if (title) leaves.push(`course:name:${title}`);
+
+ const gradeText = normalize($.text().match(/grade achieved:\s*\d+(?:\.\d+)?%/i)?.[0] || "");
+ const gradePercent = parsePercent(gradeText);
+ if (gradePercent !== null) leaves.push(`course:grade:${gradePercent}%`);
+ }
+
+ $(`meta[${nonceLeafId}]`).each((_, node) => {
+ const nonceValue = String($(node).attr(nonceLeafId) || "").trim();
+ if (nonceValue) {
+ leaves.push(`nonce:id:${nonceLeafId}:value:${nonceValue}`);
+ }
+ });
+
+ return [...new Set(leaves)].sort();
+ },
+};
diff --git a/packages/gbv-core/src/receipt.ts b/packages/gbv-core/src/receipt.ts
new file mode 100644
index 0000000..88dab39
--- /dev/null
+++ b/packages/gbv-core/src/receipt.ts
@@ -0,0 +1,52 @@
+import crypto from "node:crypto";
+import type { GbvReceiptPayload } from "./types";
+
+function base64UrlEncode(value: string | Buffer): string {
+ return Buffer.from(value)
+ .toString("base64")
+ .replace(/\+/g, "-")
+ .replace(/\//g, "_")
+ .replace(/=+$/g, "");
+}
+
+function base64UrlDecode(value: string): string {
+ const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
+ const padded = normalized + "===".slice((normalized.length + 3) % 4);
+ return Buffer.from(padded, "base64").toString("utf8");
+}
+
+/**
+ * Spec §Stage V: produce HMAC-signed receipt token.
+ *
+ * @param {GbvReceiptPayload} payload Receipt payload.
+ * @param {string} secret Signing secret.
+ * @returns {string} Compact receipt token.
+ */
+export function signReceipt(payload: GbvReceiptPayload, secret: string): string {
+ const serialized = JSON.stringify(payload);
+ const body = base64UrlEncode(serialized);
+ const signature = crypto.createHmac("sha256", secret).update(body).digest();
+ return `${body}.${base64UrlEncode(signature)}`;
+}
+
+/**
+ * Verify and decode a signed receipt token.
+ *
+ * @param {string} token Compact token.
+ * @param {string} secret Signing secret.
+ * @returns {GbvReceiptPayload} Decoded payload.
+ */
+export function verifyReceipt(token: string, secret: string): GbvReceiptPayload {
+ const [body, signature] = String(token || "").split(".");
+ if (!body || !signature) {
+ throw new Error("Invalid GBV receipt token format");
+ }
+
+ const expected = base64UrlEncode(crypto.createHmac("sha256", secret).update(body).digest());
+ if (expected !== signature) {
+ throw new Error("Invalid GBV receipt signature");
+ }
+
+ const parsed = JSON.parse(base64UrlDecode(body));
+ return parsed as GbvReceiptPayload;
+}
diff --git a/packages/gbv-core/src/semantic/verifyProviderConsistency.ts b/packages/gbv-core/src/semantic/verifyProviderConsistency.ts
new file mode 100644
index 0000000..e360b38
--- /dev/null
+++ b/packages/gbv-core/src/semantic/verifyProviderConsistency.ts
@@ -0,0 +1,231 @@
+import type { GbvPageBundle, GbvSemanticReport } from "../types";
+
+function valueSet(leaves: string[], prefix: string): Set {
+ const output = new Set();
+ const marker = `${prefix}:`;
+
+ for (const leaf of leaves) {
+ if (leaf.startsWith(marker)) {
+ output.add(leaf.slice(marker.length));
+ }
+ }
+
+ return output;
+}
+
+function parsePercent(input: string): number | null {
+ const match = input.match(/(\d+(?:\.\d+)?)\s*%/);
+ if (!match) return null;
+ const parsed = Number.parseFloat(match[1]);
+ return Number.isFinite(parsed) ? parsed : null;
+}
+
+function parseNumber(input: string): number | null {
+ const parsed = Number.parseFloat(String(input || ""));
+ return Number.isFinite(parsed) ? parsed : null;
+}
+
+function parseNonceLeaves(leaves: string[]): Array<{ id: string; value: string }> {
+ const result: Array<{ id: string; value: string }> = [];
+
+ for (const leaf of leaves) {
+ const segments = leaf.split(":");
+ if (segments.length >= 5 && segments[0] === "nonce" && segments[1] === "id") {
+ result.push({
+ id: segments[2],
+ value: segments.slice(4).join(":"),
+ });
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Spec Stage III: semantic coherence enforcement over merged canonical leaves.
+ */
+export function verifyProviderConsistency({
+ provider,
+ bundles,
+ minGradePercent,
+ expectedNonces,
+}: {
+ provider: string;
+ bundles: GbvPageBundle[];
+ minGradePercent: number;
+ expectedNonces: Array<{ id: string; value: string }>;
+}): GbvSemanticReport {
+ const invariants: GbvSemanticReport["invariants"] = [];
+ const errors: string[] = [];
+ const warnings: string[] = [];
+ const info: string[] = [];
+
+ const addInvariant = (
+ id: string,
+ status: "pass" | "fail" | "warn",
+ detail: string,
+ ) => {
+ invariants.push({ id, status, detail });
+ if (status === "fail") errors.push(`${id}: ${detail}`);
+ if (status === "warn") warnings.push(`${id}: ${detail}`);
+ if (status === "pass") info.push(`${id}: ${detail}`);
+ };
+
+ const leaves = bundles.flatMap((bundle) => bundle.leaves);
+ const providers = [...valueSet(leaves, "provider")];
+ const pageTypes = [...valueSet(leaves, "page:type")];
+ const courseIds = [...valueSet(leaves, "course:id")];
+ const courseKeys = [...valueSet(leaves, "course:key")];
+ const courseSlugs = [...valueSet(leaves, "course:slug")];
+ const courseNames = [...valueSet(leaves, "course:name")];
+ const certificateIds = [...valueSet(leaves, "certificate:id")];
+ const gradeLeaves = [...valueSet(leaves, "course:grade")];
+ const moduleCountLeaves = [...valueSet(leaves, "course:module-count")];
+ const gradePercents = gradeLeaves
+ .map((leaf) => parsePercent(leaf))
+ .filter((percent): percent is number => percent !== null);
+ const moduleCounts = moduleCountLeaves
+ .map((leaf) => parseNumber(leaf))
+ .filter((count): count is number => count !== null);
+ const observedNonces = parseNonceLeaves(leaves);
+
+ if (providers.includes(provider)) {
+ addInvariant("provider_id", "pass", `Observed provider '${provider}'`);
+ } else {
+ addInvariant(
+ "provider_id",
+ "fail",
+ `Expected '${provider}', observed [${providers.join(", ")}]`,
+ );
+ }
+
+ if (courseIds.length <= 1) {
+ addInvariant("course_id_consistency", "pass", `Observed ${courseIds.length || 0} unique value`);
+ } else {
+ addInvariant(
+ "course_id_consistency",
+ "fail",
+ `Multiple course IDs observed: [${courseIds.join(", ")}]`,
+ );
+ }
+
+ if (courseKeys.length <= 1) {
+ addInvariant("course_key_consistency", "pass", `Observed ${courseKeys.length || 0} unique value`);
+ } else {
+ addInvariant(
+ "course_key_consistency",
+ "fail",
+ `Multiple course keys observed: [${courseKeys.join(", ")}]`,
+ );
+ }
+
+ if (courseSlugs.length <= 1) {
+ addInvariant("course_slug_consistency", "pass", `Observed ${courseSlugs.length || 0} unique value`);
+ } else {
+ addInvariant(
+ "course_slug_consistency",
+ "fail",
+ `Multiple course slugs observed: [${courseSlugs.join(", ")}]`,
+ );
+ }
+
+ if (courseNames.length <= 1) {
+ addInvariant("course_name_consistency", "pass", `Observed ${courseNames.length || 0} unique value`);
+ } else {
+ addInvariant(
+ "course_name_consistency",
+ "fail",
+ `Multiple course names observed: [${courseNames.join(", ")}]`,
+ );
+ }
+
+ if (certificateIds.length <= 1) {
+ addInvariant(
+ "certificate_id_consistency",
+ "pass",
+ `Observed ${certificateIds.length || 0} unique value`,
+ );
+ } else {
+ addInvariant(
+ "certificate_id_consistency",
+ "fail",
+ `Multiple certificate IDs observed: [${certificateIds.join(", ")}]`,
+ );
+ }
+
+ if (!moduleCounts.length) {
+ addInvariant("module_count_consistency", "warn", "No module-count semantic fields were observed");
+ } else {
+ const uniqueModuleCounts = [...new Set(moduleCounts)];
+ if (uniqueModuleCounts.length === 1) {
+ addInvariant(
+ "module_count_consistency",
+ "pass",
+ `Observed consistent module count ${uniqueModuleCounts[0]}`,
+ );
+ } else {
+ addInvariant(
+ "module_count_consistency",
+ "fail",
+ `Contradictory module counts observed: [${uniqueModuleCounts.join(", ")}]`,
+ );
+ }
+ }
+
+ if (!gradePercents.length) {
+ addInvariant("grade_threshold", "fail", "No grade percentage observed in canonical leaves");
+ } else {
+ const minObserved = Math.min(...gradePercents);
+ if (minObserved < minGradePercent) {
+ addInvariant(
+ "grade_threshold",
+ "fail",
+ `Observed minimum grade ${minObserved}% is below threshold ${minGradePercent}%`,
+ );
+ } else {
+ addInvariant(
+ "grade_threshold",
+ "pass",
+ `Observed minimum grade ${minObserved}% satisfies threshold ${minGradePercent}%`,
+ );
+ }
+ }
+
+ if (!expectedNonces.length) {
+ addInvariant("nonce_presence", "warn", "No expected nonces were supplied to semantic verification");
+ } else {
+ const missingExpected = expectedNonces.filter(
+ (expected) =>
+ !observedNonces.some(
+ (nonce) => nonce.id === expected.id && nonce.value === expected.value,
+ ),
+ );
+
+ if (!missingExpected.length) {
+ addInvariant("nonce_presence", "pass", "All expected challenge nonces were observed");
+ } else {
+ addInvariant(
+ "nonce_presence",
+ "fail",
+ `Missing ${missingExpected.length} expected challenge nonce value(s)`,
+ );
+ }
+ }
+
+ const score = Math.max(0, 100 - errors.length * 15 - warnings.length * 3);
+
+ return {
+ ok: errors.length === 0,
+ score,
+ invariants,
+ findings: { errors, warnings, info },
+ observed: {
+ providers,
+ pageTypes,
+ courseNames,
+ certificateIds,
+ gradePercents,
+ nonces: observedNonces,
+ },
+ };
+}
diff --git a/packages/gbv-core/src/semantic/verifyStructureBoundNonces.ts b/packages/gbv-core/src/semantic/verifyStructureBoundNonces.ts
new file mode 100644
index 0000000..94bdbec
--- /dev/null
+++ b/packages/gbv-core/src/semantic/verifyStructureBoundNonces.ts
@@ -0,0 +1,80 @@
+import type { GbvPageBundle, GbvPageType } from "../types";
+
+/**
+ * Spec §Stage III: enforce structure-bound nonces and plan coverage.
+ */
+export function verifyStructureBoundNonces({
+ bundles,
+ pagePlan,
+ pageNonces,
+ nonceLeafId,
+}: {
+ bundles: GbvPageBundle[];
+ pagePlan: GbvPageType[];
+ pageNonces: string[];
+ nonceLeafId: string;
+}): { ok: true } | { ok: false; error: string; details?: Record } {
+ if (!Array.isArray(pagePlan) || !Array.isArray(pageNonces) || pagePlan.length !== pageNonces.length) {
+ return { ok: false, error: "INVALID_NONCE_PLAN" };
+ }
+
+ const expectedByType = new Map();
+ for (let i = 0; i < pagePlan.length; i += 1) {
+ expectedByType.set(pagePlan[i], pageNonces[i]);
+ }
+
+ const seenTypes = new Set();
+
+ for (const bundle of bundles) {
+ const expectedNonce = expectedByType.get(bundle.pageType);
+ if (!expectedNonce) {
+ return {
+ ok: false,
+ error: "UNEXPECTED_PAGE_TYPE",
+ details: { pageType: bundle.pageType, url: bundle.url },
+ };
+ }
+
+ if (seenTypes.has(bundle.pageType)) {
+ return {
+ ok: false,
+ error: "DUPLICATE_PAGE_TYPE",
+ details: { pageType: bundle.pageType },
+ };
+ }
+ seenTypes.add(bundle.pageType);
+
+ if (bundle.injectedNonce !== expectedNonce) {
+ return {
+ ok: false,
+ error: "INJECTED_NONCE_MISMATCH",
+ details: {
+ pageType: bundle.pageType,
+ injectedNonce: bundle.injectedNonce,
+ expectedNonce,
+ },
+ };
+ }
+
+ const nonceLeaf = `nonce:id:${nonceLeafId}:value:${expectedNonce}`;
+ if (!bundle.leaves.includes(nonceLeaf)) {
+ return {
+ ok: false,
+ error: "NONCE_LEAF_MISSING",
+ details: { pageType: bundle.pageType, nonceLeaf },
+ };
+ }
+ }
+
+ for (const expectedType of expectedByType.keys()) {
+ if (!seenTypes.has(expectedType)) {
+ return {
+ ok: false,
+ error: "MISSING_PAGE_TYPE",
+ details: { pageType: expectedType },
+ };
+ }
+ }
+
+ return { ok: true };
+}
diff --git a/packages/gbv-core/src/surfacePlan.ts b/packages/gbv-core/src/surfacePlan.ts
new file mode 100644
index 0000000..99cba7b
--- /dev/null
+++ b/packages/gbv-core/src/surfacePlan.ts
@@ -0,0 +1,55 @@
+import type { GbvPageType, GbvVerificationArtifact } from "./types";
+
+export type GbvSurfacePathTemplates = {
+ hub: string;
+ course: string;
+ assignments: string;
+ proof: string;
+ certificate: string;
+ milestones: string;
+};
+
+function applyArtifactTemplate(
+ template: string,
+ artifact: GbvVerificationArtifact,
+): string {
+ return template
+ .replaceAll(":publicCourseKey", encodeURIComponent(artifact.publicCourseKey))
+ .replaceAll(":certificateId", encodeURIComponent(artifact.certificateId))
+ .replaceAll(":courseId", encodeURIComponent(String(artifact.courseId)));
+}
+
+function joinOriginAndPath(origin: string, path: string): string {
+ const normalizedOrigin = origin.replace(/\/+$/g, "");
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
+ return `${normalizedOrigin}${normalizedPath}`;
+}
+
+/**
+ * Build planned surface URLs from configured path templates and selected artifact metadata.
+ */
+export function buildSurfaceUrlsFromPlan({
+ origin,
+ pagePlan,
+ templates,
+ artifact,
+}: {
+ origin: string;
+ pagePlan: GbvPageType[];
+ templates: GbvSurfacePathTemplates;
+ artifact: GbvVerificationArtifact;
+}): string[] {
+ return pagePlan.map((pageType) => {
+ if (pageType === "unknown") {
+ throw new Error("Cannot resolve URL template for unknown page type");
+ }
+
+ const pathTemplate = templates[pageType];
+ if (!pathTemplate) {
+ throw new Error(`Missing URL path template for page type '${pageType}'`);
+ }
+
+ const resolvedPath = applyArtifactTemplate(pathTemplate, artifact);
+ return joinOriginAndPath(origin, resolvedPath);
+ });
+}
diff --git a/packages/gbv-core/src/types.ts b/packages/gbv-core/src/types.ts
new file mode 100644
index 0000000..2ab2c68
--- /dev/null
+++ b/packages/gbv-core/src/types.ts
@@ -0,0 +1,103 @@
+/** Supported GBV page types in the synthetic demo. */
+export type GbvPageType =
+ | "hub"
+ | "course"
+ | "assignments"
+ | "proof"
+ | "certificate"
+ | "milestones"
+ | "unknown";
+
+/** Artifact selected by the verifier client for observation. */
+export interface GbvVerificationArtifact {
+ courseId: number;
+ publicCourseKey: string;
+ certificateId: string;
+ title?: string;
+}
+
+/** Canonicalized evidence bundle for a single observed surface. */
+export interface GbvPageBundle {
+ index: number;
+ pageType: GbvPageType;
+ url: string;
+ html: string;
+ injectedNonce: string;
+ leaves: string[];
+}
+
+/** Stage I session created by the verifier. */
+export interface GbvSession {
+ sessionId: string;
+ challengeNonce: string;
+ provider: string;
+ artifact: GbvVerificationArtifact;
+ pagePlan: GbvPageType[];
+ pageNonces: string[];
+ nonceLeafId: string;
+ expiresAt: string;
+ used: boolean;
+}
+
+/** Generic GBV API error response shape. */
+export interface GbvApiError {
+ ok: false;
+ code: string;
+ error: string;
+ meta?: Record;
+}
+
+/** Generic accepted verification response payload. */
+export interface GbvVerificationSuccess {
+ ok: true;
+ accepted: true;
+ sessionId: string;
+ receiptId: string;
+ receipt: string;
+ provider: string;
+ artifact: GbvVerificationArtifact;
+ evidenceHash: string;
+ merkleRoot: string;
+ leafCount: number;
+ verifiedAt: string;
+ verificationLog: {
+ structure: { ok: boolean; error?: string; details?: Record };
+ semantic: GbvSemanticReport;
+ };
+}
+
+/** Semantic verification report returned by Stage III checks. */
+export interface GbvSemanticReport {
+ ok: boolean;
+ score: number;
+ invariants: Array<{
+ id: string;
+ status: "pass" | "fail" | "warn";
+ detail: string;
+ }>;
+ findings: {
+ errors: string[];
+ warnings: string[];
+ info: string[];
+ };
+ observed: {
+ providers: string[];
+ pageTypes: string[];
+ courseNames: string[];
+ certificateIds: string[];
+ gradePercents: number[];
+ nonces: Array<{ id: string; value: string }>;
+ };
+}
+
+/** Detached receipt payload signed by the verifier. */
+export interface GbvReceiptPayload {
+ receiptId: string;
+ sessionId: string;
+ artifact: GbvVerificationArtifact;
+ provider: string;
+ merkleRoot: string;
+ evidenceHash: string;
+ issuedAt: string;
+ protocolVersion: string;
+}
diff --git a/packages/gbv-core/tests/invariants.test.ts b/packages/gbv-core/tests/invariants.test.ts
new file mode 100644
index 0000000..0434b02
--- /dev/null
+++ b/packages/gbv-core/tests/invariants.test.ts
@@ -0,0 +1,41 @@
+import assert from "node:assert/strict";
+import { describe, test } from "node:test";
+import { verifyStructureBoundNonces } from "../src/semantic/verifyStructureBoundNonces";
+import type { GbvPageBundle } from "../src/types";
+
+function bundle(pageType: "hub" | "course", nonceValue: string): GbvPageBundle {
+ return {
+ index: pageType === "hub" ? 0 : 1,
+ pageType,
+ url: `http://localhost/${pageType}`,
+ html: "",
+ injectedNonce: nonceValue,
+ leaves: [`nonce:id:data-gbv-nonce:value:${nonceValue}`],
+ };
+}
+
+describe("structure-bound nonces", () => {
+ test("rejects mismatched injected nonce", () => {
+ const result = verifyStructureBoundNonces({
+ bundles: [bundle("hub", "wrong"), bundle("course", "n2")],
+ pagePlan: ["hub", "course"],
+ pageNonces: ["n1", "n2"],
+ nonceLeafId: "data-gbv-nonce",
+ });
+
+ assert.equal(result.ok, false);
+ assert.equal(result.error, "INJECTED_NONCE_MISMATCH");
+ });
+
+ test("rejects missing expected page type", () => {
+ const result = verifyStructureBoundNonces({
+ bundles: [bundle("hub", "n1")],
+ pagePlan: ["hub", "course"],
+ pageNonces: ["n1", "n2"],
+ nonceLeafId: "data-gbv-nonce",
+ });
+
+ assert.equal(result.ok, false);
+ assert.equal(result.error, "MISSING_PAGE_TYPE");
+ });
+});
diff --git a/packages/gbv-core/tests/merkle.test.ts b/packages/gbv-core/tests/merkle.test.ts
new file mode 100644
index 0000000..f37d7bc
--- /dev/null
+++ b/packages/gbv-core/tests/merkle.test.ts
@@ -0,0 +1,35 @@
+import assert from "node:assert/strict";
+import { describe, test } from "node:test";
+import { buildMerkleRoot, sha256Hex, verifyMerkleProof } from "../src/merkle";
+
+describe("merkle proofs", () => {
+ test("accepts valid proof for a two-leaf tree", () => {
+ const leaves = ["alpha", "beta"];
+ const leafHashes = leaves.map((leaf) => sha256Hex(leaf));
+ const root = buildMerkleRoot(leafHashes);
+
+ const ok = verifyMerkleProof({
+ leaf: leaves[0],
+ index: 0,
+ proof: [leafHashes[1]],
+ expectedRoot: root,
+ });
+
+ assert.equal(ok, true);
+ });
+
+ test("rejects proof with wrong expected root", () => {
+ const leaves = ["alpha", "beta"];
+ const leafHashes = leaves.map((leaf) => sha256Hex(leaf));
+ const root = buildMerkleRoot(leafHashes);
+
+ const ok = verifyMerkleProof({
+ leaf: leaves[0],
+ index: 0,
+ proof: [leafHashes[1]],
+ expectedRoot: root.replace(/^./, "f"),
+ });
+
+ assert.equal(ok, false);
+ });
+});
diff --git a/packages/gbv-core/tests/receipt.test.ts b/packages/gbv-core/tests/receipt.test.ts
new file mode 100644
index 0000000..18b2a33
--- /dev/null
+++ b/packages/gbv-core/tests/receipt.test.ts
@@ -0,0 +1,35 @@
+import assert from "node:assert/strict";
+import { describe, test } from "node:test";
+import { signReceipt, verifyReceipt } from "../src/receipt";
+import type { GbvReceiptPayload } from "../src/types";
+
+const secret = "unit-test-secret";
+
+const payload: GbvReceiptPayload = {
+ receiptId: "rid-1",
+ sessionId: "sid-1",
+ artifact: {
+ courseId: 1,
+ publicCourseKey: "csk_test",
+ certificateId: "cert-1",
+ },
+ provider: "synthetic",
+ merkleRoot: "a".repeat(64),
+ evidenceHash: "b".repeat(64),
+ issuedAt: "2026-02-14T00:00:00.000Z",
+ protocolVersion: "0.71",
+};
+
+describe("receipt signing", () => {
+ test("verifies a valid signed receipt", () => {
+ const token = signReceipt(payload, secret);
+ const decoded = verifyReceipt(token, secret);
+ assert.deepEqual(decoded, payload);
+ });
+
+ test("rejects a tampered receipt signature", () => {
+ const token = signReceipt(payload, secret);
+ const [body] = token.split(".");
+ assert.throws(() => verifyReceipt(`${body}.tampered`, secret));
+ });
+});
diff --git a/packages/gbv-core/tsconfig.json b/packages/gbv-core/tsconfig.json
new file mode 100644
index 0000000..c3e2f77
--- /dev/null
+++ b/packages/gbv-core/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "composite": true
+ },
+ "include": ["src/**/*.ts"]
+}
\ No newline at end of file
diff --git a/packages/providers/README.md b/packages/providers/README.md
deleted file mode 100644
index ae4c686..0000000
--- a/packages/providers/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Providers
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..b4ba3e1
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,6139 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ '@types/node':
+ specifier: ^22.13.4
+ version: 22.19.11
+ chrome-launcher:
+ specifier: ^1.2.1
+ version: 1.2.1
+ playwright:
+ specifier: ^1.51.1
+ version: 1.58.2
+ prettier:
+ specifier: ^3.5.2
+ version: 3.8.1
+ puppeteer-core:
+ specifier: ^24.26.1
+ version: 24.37.3
+ rimraf:
+ specifier: ^6.0.1
+ version: 6.1.2
+ tsx:
+ specifier: ^4.19.3
+ version: 4.21.0
+ typescript:
+ specifier: ^5.7.3
+ version: 5.9.3
+
+ apps/client/synthetic:
+ dependencies:
+ '@gbv/config':
+ specifier: workspace:*
+ version: link:../../../packages/gbv-config
+ next:
+ specifier: 16.1.1
+ version: 16.1.1(@babel/core@7.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react:
+ specifier: 19.2.3
+ version: 19.2.3
+ react-dom:
+ specifier: 19.2.3
+ version: 19.2.3(react@19.2.3)
+ devDependencies:
+ '@tailwindcss/postcss':
+ specifier: ^4
+ version: 4.1.18
+ eslint:
+ specifier: ^9.38.0
+ version: 9.39.2(jiti@2.6.1)
+ eslint-config-next:
+ specifier: 16.1.1
+ version: 16.1.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ tailwindcss:
+ specifier: ^4
+ version: 4.1.18
+
+ apps/extension:
+ dependencies:
+ '@gbv/config':
+ specifier: workspace:*
+ version: link:../../packages/gbv-config
+ '@gbv/core':
+ specifier: workspace:*
+ version: link:../../packages/gbv-core
+ react:
+ specifier: 19.2.3
+ version: 19.2.3
+ react-dom:
+ specifier: 19.2.3
+ version: 19.2.3(react@19.2.3)
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.19.0
+ version: 9.39.2
+ '@types/chrome':
+ specifier: ^0.0.315
+ version: 0.0.315
+ '@types/react':
+ specifier: ^19.0.8
+ version: 19.2.14
+ '@types/react-dom':
+ specifier: ^19.0.3
+ version: 19.2.3(@types/react@19.2.14)
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.46.2
+ version: 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser':
+ specifier: ^8.46.2
+ version: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ '@vitejs/plugin-react-swc':
+ specifier: ^3.8.0
+ version: 3.11.0(vite@6.4.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))
+ eslint:
+ specifier: ^9.19.0
+ version: 9.39.2(jiti@2.6.1)
+ globals:
+ specifier: ^15.14.0
+ version: 15.15.0
+ tsx:
+ specifier: ^4.21.0
+ version: 4.21.0
+ typescript:
+ specifier: ^5.7.3
+ version: 5.9.3
+ vite:
+ specifier: ^6.0.11
+ version: 6.4.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+
+ apps/server:
+ dependencies:
+ '@gbv/config':
+ specifier: workspace:*
+ version: link:../../packages/gbv-config
+ '@gbv/core':
+ specifier: workspace:*
+ version: link:../../packages/gbv-core
+ next:
+ specifier: 16.1.1
+ version: 16.1.1(@babel/core@7.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react:
+ specifier: 19.2.3
+ version: 19.2.3
+ react-dom:
+ specifier: 19.2.3
+ version: 19.2.3(react@19.2.3)
+ devDependencies:
+ eslint:
+ specifier: ^9.38.0
+ version: 9.39.2(jiti@2.6.1)
+ eslint-config-next:
+ specifier: 16.1.1
+ version: 16.1.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+
+ packages/gbv-config: {}
+
+ packages/gbv-core:
+ dependencies:
+ cheerio:
+ specifier: ^1.1.2
+ version: 1.2.0
+
+packages:
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.0':
+ resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.0':
+ resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.1':
+ resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.6':
+ resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.0':
+ resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.0':
+ resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
+ engines: {node: '>=6.9.0'}
+
+ '@emnapi/core@1.8.1':
+ resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
+
+ '@emnapi/runtime@1.8.1':
+ resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==}
+
+ '@emnapi/wasi-threads@1.1.0':
+ resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
+
+ '@esbuild/aix-ppc64@0.25.12':
+ resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.27.3':
+ resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.12':
+ resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.27.3':
+ resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.12':
+ resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.3':
+ resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.12':
+ resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.3':
+ resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.12':
+ resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.27.3':
+ resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.12':
+ resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.3':
+ resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.27.3':
+ resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.12':
+ resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.3':
+ resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.12':
+ resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.27.3':
+ resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.12':
+ resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.3':
+ resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.12':
+ resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.3':
+ resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.12':
+ resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.3':
+ resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.12':
+ resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.3':
+ resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.12':
+ resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.3':
+ resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.12':
+ resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.3':
+ resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.12':
+ resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.3':
+ resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.12':
+ resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.3':
+ resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.12':
+ resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.3':
+ resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.12':
+ resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.3':
+ resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/openharmony-arm64@0.27.3':
+ resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.12':
+ resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.27.3':
+ resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.12':
+ resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.27.3':
+ resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.12':
+ resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.3':
+ resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.12':
+ resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.3':
+ resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@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}
+
+ '@eslint/config-array@0.21.1':
+ resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.4.2':
+ resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.17.0':
+ resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.3.3':
+ resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.39.2':
+ resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.7':
+ resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.4.1':
+ resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.7':
+ resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@img/colour@1.0.0':
+ resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
+ engines: {node: '>=18'}
+
+ '@img/sharp-darwin-arm64@0.34.5':
+ resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.34.5':
+ resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.2.4':
+ resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.2.4':
+ resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.2.4':
+ resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-arm@1.2.4':
+ resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-ppc64@1.2.4':
+ resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-riscv64@1.2.4':
+ resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-s390x@1.2.4':
+ resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-x64@1.2.4':
+ resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
+ resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.4':
+ resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linux-arm64@0.34.5':
+ resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linux-arm@0.34.5':
+ resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-linux-ppc64@0.34.5':
+ resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@img/sharp-linux-riscv64@0.34.5':
+ resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@img/sharp-linux-s390x@0.34.5':
+ resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-linux-x64@0.34.5':
+ resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-arm64@0.34.5':
+ resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-x64@0.34.5':
+ resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-wasm32@0.34.5':
+ resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-arm64@0.34.5':
+ resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@img/sharp-win32-ia32@0.34.5':
+ resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.34.5':
+ resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@isaacs/cliui@9.0.0':
+ resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==}
+ engines: {node: '>=18'}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
+
+ '@next/env@16.1.1':
+ resolution: {integrity: sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==}
+
+ '@next/eslint-plugin-next@16.1.1':
+ resolution: {integrity: sha512-Ovb/6TuLKbE1UiPcg0p39Ke3puyTCIKN9hGbNItmpQsp+WX3qrjO3WaMVSi6JHr9X1NrmthqIguVHodMJbh/dw==}
+
+ '@next/swc-darwin-arm64@16.1.1':
+ resolution: {integrity: sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@16.1.1':
+ resolution: {integrity: sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@16.1.1':
+ resolution: {integrity: sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-arm64-musl@16.1.1':
+ resolution: {integrity: sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-x64-gnu@16.1.1':
+ resolution: {integrity: sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-linux-x64-musl@16.1.1':
+ resolution: {integrity: sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-win32-arm64-msvc@16.1.1':
+ resolution: {integrity: sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@16.1.1':
+ resolution: {integrity: sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@nolyfill/is-core-module@1.0.39':
+ resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
+ engines: {node: '>=12.4.0'}
+
+ '@puppeteer/browsers@2.12.1':
+ resolution: {integrity: sha512-fXa6uXLxfslBlus3MEpW8S6S9fe5RwmAE5Gd8u3krqOwnkZJV3/lQJiY3LaFdTctLLqJtyMgEUGkbDnRNf6vbQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ '@rolldown/pluginutils@1.0.0-beta.27':
+ resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
+
+ '@rollup/rollup-android-arm-eabi@4.57.1':
+ resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.57.1':
+ resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.57.1':
+ resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.57.1':
+ resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.57.1':
+ resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.57.1':
+ resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.57.1':
+ resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.57.1':
+ resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.57.1':
+ resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.57.1':
+ resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.57.1':
+ resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-musl@4.57.1':
+ resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.57.1':
+ resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-musl@4.57.1':
+ resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.57.1':
+ resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.57.1':
+ resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.57.1':
+ resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.57.1':
+ resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.57.1':
+ resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openbsd-x64@4.57.1':
+ resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.57.1':
+ resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.57.1':
+ resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.57.1':
+ resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.57.1':
+ resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.57.1':
+ resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rtsao/scc@1.1.0':
+ resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+
+ '@swc/core-darwin-arm64@1.15.11':
+ resolution: {integrity: sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@swc/core-darwin-x64@1.15.11':
+ resolution: {integrity: sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@swc/core-linux-arm-gnueabihf@1.15.11':
+ resolution: {integrity: sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@swc/core-linux-arm64-gnu@1.15.11':
+ resolution: {integrity: sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-arm64-musl@1.15.11':
+ resolution: {integrity: sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-x64-gnu@1.15.11':
+ resolution: {integrity: sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-linux-x64-musl@1.15.11':
+ resolution: {integrity: sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-win32-arm64-msvc@1.15.11':
+ resolution: {integrity: sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@swc/core-win32-ia32-msvc@1.15.11':
+ resolution: {integrity: sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@swc/core-win32-x64-msvc@1.15.11':
+ resolution: {integrity: sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@swc/core@1.15.11':
+ resolution: {integrity: sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@swc/helpers': '>=0.5.17'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
+ '@swc/types@0.1.25':
+ resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==}
+
+ '@tailwindcss/node@4.1.18':
+ resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.18':
+ resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.18':
+ resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.18':
+ resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.18':
+ resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
+ resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
+ resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
+ resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
+ resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.18':
+ resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.18':
+ resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
+ resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.18':
+ resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.18':
+ resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/postcss@4.1.18':
+ resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==}
+
+ '@tootallnate/quickjs-emscripten@0.23.0':
+ resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
+
+ '@tybys/wasm-util@0.10.1':
+ resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+
+ '@types/chrome@0.0.315':
+ resolution: {integrity: sha512-Oy1dYWkr6BCmgwBtOngLByCHstQ3whltZg7/7lubgIZEYvKobDneqplgc6LKERNRBwckFviV4UU5AZZNUFrJ4A==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/filesystem@0.0.36':
+ resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
+
+ '@types/filewriter@0.0.33':
+ resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
+
+ '@types/har-format@1.2.16':
+ resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/json5@0.0.29':
+ resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+ '@types/node@22.19.11':
+ resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.14':
+ resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
+
+ '@types/yauzl@2.10.3':
+ resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
+
+ '@typescript-eslint/eslint-plugin@8.55.0':
+ resolution: {integrity: sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.55.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/parser@8.55.0':
+ resolution: {integrity: sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/project-service@8.55.0':
+ resolution: {integrity: sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/scope-manager@8.55.0':
+ resolution: {integrity: sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.55.0':
+ resolution: {integrity: sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/type-utils@8.55.0':
+ resolution: {integrity: sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/types@8.55.0':
+ resolution: {integrity: sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.55.0':
+ resolution: {integrity: sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/utils@8.55.0':
+ resolution: {integrity: sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/visitor-keys@8.55.0':
+ resolution: {integrity: sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
+ resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
+ cpu: [arm]
+ os: [android]
+
+ '@unrs/resolver-binding-android-arm64@1.11.1':
+ resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==}
+ cpu: [arm64]
+ os: [android]
+
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
+ resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
+ resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
+ resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
+ resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
+ resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
+ resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
+ resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
+ resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
+ resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
+ cpu: [x64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
+ resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
+ resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
+ resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
+ resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==}
+ cpu: [x64]
+ os: [win32]
+
+ '@vitejs/plugin-react-swc@3.11.0':
+ resolution: {integrity: sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==}
+ peerDependencies:
+ vite: ^4 || ^5 || ^6 || ^7
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ array-buffer-byte-length@1.0.2:
+ resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
+ engines: {node: '>= 0.4'}
+
+ array-includes@3.1.9:
+ resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlast@1.2.5:
+ resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlastindex@1.2.6:
+ resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flat@1.3.3:
+ resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flatmap@1.3.3:
+ resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.tosorted@1.1.4:
+ resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
+ engines: {node: '>= 0.4'}
+
+ arraybuffer.prototype.slice@1.0.4:
+ resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
+ engines: {node: '>= 0.4'}
+
+ ast-types-flow@0.0.8:
+ resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
+
+ ast-types@0.13.4:
+ resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
+ engines: {node: '>=4'}
+
+ async-function@1.0.0:
+ resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
+ engines: {node: '>= 0.4'}
+
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
+ axe-core@4.11.1:
+ resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==}
+ engines: {node: '>=4'}
+
+ axobject-query@4.1.0:
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+ engines: {node: '>= 0.4'}
+
+ b4a@1.7.4:
+ resolution: {integrity: sha512-u20zJLDaSWpxaZ+zaAkEIB2dZZ1o+DF4T/MRbmsvGp9nletHOyiai19OzX1fF8xUBYsO1bPXxODvcd0978pnug==}
+ peerDependencies:
+ react-native-b4a: '*'
+ peerDependenciesMeta:
+ react-native-b4a:
+ optional: true
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ balanced-match@4.0.2:
+ resolution: {integrity: sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==}
+ engines: {node: 20 || >=22}
+
+ bare-events@2.8.2:
+ resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==}
+ peerDependencies:
+ bare-abort-controller: '*'
+ peerDependenciesMeta:
+ bare-abort-controller:
+ optional: true
+
+ bare-fs@4.5.4:
+ resolution: {integrity: sha512-POK4oplfA7P7gqvetNmCs4CNtm9fNsx+IAh7jH7GgU0OJdge2rso0R20TNWVq6VoWcCvsTdlNDaleLHGaKx8CA==}
+ engines: {bare: '>=1.16.0'}
+ peerDependencies:
+ bare-buffer: '*'
+ peerDependenciesMeta:
+ bare-buffer:
+ optional: true
+
+ bare-os@3.6.2:
+ resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==}
+ engines: {bare: '>=1.14.0'}
+
+ bare-path@3.0.0:
+ resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==}
+
+ bare-stream@2.7.0:
+ resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==}
+ peerDependencies:
+ bare-buffer: '*'
+ bare-events: '*'
+ peerDependenciesMeta:
+ bare-buffer:
+ optional: true
+ bare-events:
+ optional: true
+
+ bare-url@2.3.2:
+ resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==}
+
+ baseline-browser-mapping@2.9.19:
+ resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==}
+ hasBin: true
+
+ basic-ftp@5.1.0:
+ resolution: {integrity: sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==}
+ engines: {node: '>=10.0.0'}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ brace-expansion@5.0.2:
+ resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==}
+ engines: {node: 20 || >=22}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.28.1:
+ resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ buffer-crc32@0.2.13:
+ resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.8:
+ resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ caniuse-lite@1.0.30001769:
+ resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ cheerio-select@2.1.0:
+ resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
+
+ cheerio@1.2.0:
+ resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==}
+ engines: {node: '>=20.18.1'}
+
+ chrome-launcher@1.2.1:
+ resolution: {integrity: sha512-qmFR5PLMzHyuNJHwOloHPAHhbaNglkfeV/xDtt5b7xiFFyU1I+AZZX0PYseMuhenJSSirgxELYIbswcoc+5H4A==}
+ engines: {node: '>=12.13.0'}
+ hasBin: true
+
+ chromium-bidi@14.0.0:
+ resolution: {integrity: sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==}
+ peerDependencies:
+ devtools-protocol: '*'
+
+ 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'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css-select@5.2.2:
+ resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
+
+ css-what@6.2.2:
+ resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
+ engines: {node: '>= 6'}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ damerau-levenshtein@1.0.8:
+ resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
+
+ data-uri-to-buffer@6.0.2:
+ resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
+ engines: {node: '>= 14'}
+
+ data-view-buffer@1.0.2:
+ resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-length@1.0.2:
+ resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-offset@1.0.1:
+ resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
+ engines: {node: '>= 0.4'}
+
+ debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ define-properties@1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+
+ degenerator@5.0.1:
+ resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==}
+ engines: {node: '>= 14'}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ devtools-protocol@0.0.1566079:
+ resolution: {integrity: sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==}
+
+ doctrine@2.1.0:
+ resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+ engines: {node: '>=0.10.0'}
+
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
+ domutils@3.2.2:
+ resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ electron-to-chromium@1.5.286:
+ resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ encoding-sniffer@0.2.1:
+ resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==}
+
+ end-of-stream@1.4.5:
+ resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
+
+ enhanced-resolve@5.19.0:
+ resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==}
+ engines: {node: '>=10.13.0'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
+ entities@7.0.1:
+ resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
+ engines: {node: '>=0.12'}
+
+ es-abstract@1.24.1:
+ resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==}
+ engines: {node: '>= 0.4'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-iterator-helpers@1.2.2:
+ resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es-shim-unscopables@1.1.0:
+ resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
+ engines: {node: '>= 0.4'}
+
+ es-to-primitive@1.3.0:
+ resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
+ engines: {node: '>= 0.4'}
+
+ esbuild@0.25.12:
+ resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ esbuild@0.27.3:
+ resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ escodegen@2.1.0:
+ resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
+ engines: {node: '>=6.0'}
+ hasBin: true
+
+ eslint-config-next@16.1.1:
+ resolution: {integrity: sha512-55nTpVWm3qeuxoQKLOjQVciKZJUphKrNM0fCcQHAIOGl6VFXgaqeMfv0aKJhs7QtcnlAPhNVqsqRfRjeKBPIUA==}
+ peerDependencies:
+ eslint: '>=9.0.0'
+ typescript: '>=3.3.1'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ eslint-import-resolver-node@0.3.9:
+ resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+ eslint-import-resolver-typescript@3.10.1:
+ resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '*'
+ eslint-plugin-import: '*'
+ eslint-plugin-import-x: '*'
+ peerDependenciesMeta:
+ eslint-plugin-import:
+ optional: true
+ eslint-plugin-import-x:
+ optional: true
+
+ eslint-module-utils@2.12.1:
+ resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: '*'
+ eslint-import-resolver-node: '*'
+ eslint-import-resolver-typescript: '*'
+ eslint-import-resolver-webpack: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ eslint:
+ optional: true
+ eslint-import-resolver-node:
+ optional: true
+ eslint-import-resolver-typescript:
+ optional: true
+ eslint-import-resolver-webpack:
+ optional: true
+
+ eslint-plugin-import@2.32.0:
+ resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+
+ eslint-plugin-jsx-a11y@6.10.2:
+ resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
+
+ eslint-plugin-react-hooks@7.0.1:
+ resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+
+ eslint-plugin-react@7.37.5:
+ resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+
+ eslint-scope@8.4.0:
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.1:
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.39.2:
+ resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.4.0:
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ events-universal@1.0.1:
+ resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==}
+
+ extract-zip@2.0.1:
+ resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
+ engines: {node: '>= 10.17.0'}
+ hasBin: true
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-fifo@1.3.2:
+ resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
+
+ fast-glob@3.3.1:
+ resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.20.1:
+ resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
+
+ fd-slicer@1.1.0:
+ resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ for-each@0.3.5:
+ resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+ engines: {node: '>= 0.4'}
+
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ function.prototype.name@1.1.8:
+ resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
+ engines: {node: '>= 0.4'}
+
+ functions-have-names@1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+ generator-function@2.0.1:
+ resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==}
+ engines: {node: '>= 0.4'}
+
+ gensync@1.0.0-beta.2:
+ 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'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-stream@5.2.0:
+ resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
+ engines: {node: '>=8'}
+
+ get-symbol-description@1.1.0:
+ resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.13.6:
+ resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==}
+
+ get-uri@6.0.5:
+ resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==}
+ engines: {node: '>= 14'}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@13.0.3:
+ resolution: {integrity: sha512-/g3B0mC+4x724v1TgtBlBtt2hPi/EWptsIAmXUx9Z2rvBYleQcsrmaOzd5LyL50jf/Soi83ZDJmw2+XqvH/EeA==}
+ engines: {node: 20 || >=22}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@15.15.0:
+ resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
+ engines: {node: '>=18'}
+
+ globals@16.4.0:
+ resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
+ engines: {node: '>=18'}
+
+ globalthis@1.0.4:
+ resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+ engines: {node: '>= 0.4'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ has-bigints@1.1.0:
+ resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
+ engines: {node: '>= 0.4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-proto@1.2.0:
+ resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
+ engines: {node: '>= 0.4'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ hermes-estree@0.25.1:
+ resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
+
+ hermes-parser@0.25.1:
+ resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
+
+ htmlparser2@10.1.0:
+ resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ internal-slot@1.1.0:
+ resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
+ engines: {node: '>= 0.4'}
+
+ ip-address@10.1.0:
+ resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
+ engines: {node: '>= 12'}
+
+ is-array-buffer@3.0.5:
+ resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
+ engines: {node: '>= 0.4'}
+
+ is-async-function@2.1.1:
+ resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
+ engines: {node: '>= 0.4'}
+
+ is-bigint@1.1.0:
+ resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
+ engines: {node: '>= 0.4'}
+
+ is-boolean-object@1.2.2:
+ resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
+ engines: {node: '>= 0.4'}
+
+ is-bun-module@2.0.0:
+ resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==}
+
+ is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-data-view@1.0.2:
+ resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
+ engines: {node: '>= 0.4'}
+
+ is-date-object@1.1.0:
+ resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
+ engines: {node: '>= 0.4'}
+
+ is-docker@2.2.1:
+ resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-finalizationregistry@1.1.1:
+ resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
+ engines: {node: '>= 0.4'}
+
+ 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'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-map@2.0.3:
+ resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
+ engines: {node: '>= 0.4'}
+
+ is-negative-zero@2.0.3:
+ resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+ engines: {node: '>= 0.4'}
+
+ is-number-object@1.1.1:
+ resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
+ engines: {node: '>= 0.4'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-regex@1.2.1:
+ resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
+ engines: {node: '>= 0.4'}
+
+ is-set@2.0.3:
+ resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+ engines: {node: '>= 0.4'}
+
+ is-shared-array-buffer@1.0.4:
+ resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
+ engines: {node: '>= 0.4'}
+
+ is-string@1.1.1:
+ resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
+ engines: {node: '>= 0.4'}
+
+ is-symbol@1.1.1:
+ resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
+ engines: {node: '>= 0.4'}
+
+ is-typed-array@1.1.15:
+ resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+ engines: {node: '>= 0.4'}
+
+ is-weakmap@2.0.2:
+ resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
+ engines: {node: '>= 0.4'}
+
+ is-weakref@1.1.1:
+ resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
+ engines: {node: '>= 0.4'}
+
+ is-weakset@2.0.4:
+ resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
+ engines: {node: '>= 0.4'}
+
+ is-wsl@2.2.0:
+ resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+ engines: {node: '>=8'}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ iterator.prototype@1.1.5:
+ resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
+ engines: {node: '>= 0.4'}
+
+ jackspeak@4.2.3:
+ resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==}
+ engines: {node: 20 || >=22}
+
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@4.1.1:
+ resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsx-ast-utils@3.3.5:
+ resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
+ engines: {node: '>=4.0'}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ language-subtag-registry@0.3.23:
+ resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
+
+ language-tags@1.0.9:
+ resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
+ engines: {node: '>=0.10'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lighthouse-logger@2.0.2:
+ resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==}
+
+ lightningcss-android-arm64@1.30.2:
+ resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.30.2:
+ resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.2:
+ resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.2:
+ resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.2:
+ resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.2:
+ resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.30.2:
+ resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.30.2:
+ resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.30.2:
+ resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.30.2:
+ resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.2:
+ resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.2:
+ resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
+ engines: {node: '>= 12.0.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ lru-cache@11.2.6:
+ resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==}
+ engines: {node: 20 || >=22}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lru-cache@7.18.3:
+ resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
+ engines: {node: '>=12'}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ marky@1.3.0:
+ resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ minimatch@10.2.0:
+ resolution: {integrity: sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==}
+ engines: {node: 20 || >=22}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ napi-postinstall@0.3.4:
+ resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ netmask@2.0.2:
+ resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
+ engines: {node: '>= 0.4.0'}
+
+ next@16.1.1:
+ resolution: {integrity: sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==}
+ engines: {node: '>=20.9.0'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ object-keys@1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+
+ object.assign@4.1.7:
+ resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
+ engines: {node: '>= 0.4'}
+
+ object.entries@1.1.9:
+ resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
+ engines: {node: '>= 0.4'}
+
+ object.fromentries@2.0.8:
+ resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
+ engines: {node: '>= 0.4'}
+
+ object.groupby@1.0.3:
+ resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
+ engines: {node: '>= 0.4'}
+
+ object.values@1.2.1:
+ resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
+ engines: {node: '>= 0.4'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ own-keys@1.0.1:
+ resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+ engines: {node: '>= 0.4'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ pac-proxy-agent@7.2.0:
+ resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==}
+ engines: {node: '>= 14'}
+
+ pac-resolver@7.0.1:
+ resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==}
+ engines: {node: '>= 14'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse5-htmlparser2-tree-adapter@7.1.0:
+ resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==}
+
+ parse5-parser-stream@7.1.2:
+ resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==}
+
+ parse5@7.3.0:
+ resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ 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@2.0.1:
+ resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==}
+ engines: {node: 20 || >=22}
+
+ pend@1.2.0:
+ resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ playwright-core@1.58.2:
+ resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.58.2:
+ resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ possible-typed-array-names@1.1.0:
+ resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
+ engines: {node: '>= 0.4'}
+
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier@3.8.1:
+ resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ progress@2.0.3:
+ resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
+ engines: {node: '>=0.4.0'}
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ proxy-agent@6.5.0:
+ resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==}
+ engines: {node: '>= 14'}
+
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ pump@3.0.3:
+ resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ puppeteer-core@24.37.3:
+ resolution: {integrity: sha512-fokQ8gv+hNgsRWqVuP5rUjGp+wzV5aMTP3fcm8ekNabmLGlJdFHas1OdMscAH9Gzq4Qcf7cfI/Pe6wEcAqQhqg==}
+ engines: {node: '>=18'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-dom@19.2.3:
+ resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==}
+ peerDependencies:
+ react: ^19.2.3
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react@19.2.3:
+ resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
+ engines: {node: '>=0.10.0'}
+
+ reflect.getprototypeof@1.0.10:
+ resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
+ engines: {node: '>= 0.4'}
+
+ regexp.prototype.flags@1.5.4:
+ resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
+ engines: {node: '>= 0.4'}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve@1.22.11:
+ resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ resolve@2.0.0-next.5:
+ resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
+ hasBin: true
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rimraf@6.1.2:
+ resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==}
+ engines: {node: 20 || >=22}
+ hasBin: true
+
+ rollup@4.57.1:
+ resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safe-array-concat@1.1.3:
+ resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
+ engines: {node: '>=0.4'}
+
+ safe-push-apply@1.0.0:
+ resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
+ engines: {node: '>= 0.4'}
+
+ safe-regex-test@1.1.0:
+ resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
+ engines: {node: '>= 0.4'}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.7.4:
+ resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ set-function-name@2.0.2:
+ resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
+ engines: {node: '>= 0.4'}
+
+ set-proto@1.0.0:
+ resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
+ engines: {node: '>= 0.4'}
+
+ sharp@0.34.5:
+ resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
+ 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.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ smart-buffer@4.2.0:
+ resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
+ engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+
+ socks-proxy-agent@8.0.5:
+ resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==}
+ engines: {node: '>= 14'}
+
+ socks@2.8.7:
+ resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==}
+ engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ stable-hash@0.0.5:
+ resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
+
+ stop-iteration-iterator@1.1.0:
+ resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
+ engines: {node: '>= 0.4'}
+
+ streamx@2.23.0:
+ resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string.prototype.includes@2.0.1:
+ resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.matchall@4.0.12:
+ resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.repeat@1.0.0:
+ resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
+
+ string.prototype.trim@1.2.10:
+ resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimend@1.0.9:
+ resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimstart@1.0.8:
+ resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ engines: {node: '>= 0.4'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ tailwindcss@4.1.18:
+ resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
+
+ tapable@2.3.0:
+ resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
+ engines: {node: '>=6'}
+
+ tar-fs@3.1.1:
+ resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==}
+
+ tar-stream@3.1.7:
+ resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
+
+ text-decoder@1.2.6:
+ resolution: {integrity: sha512-27FeW5GQFDfw0FpwMQhMagB7BztOOlmjcSRi97t2oplhKVTZtp0DZbSegSaXS5IIC6mxMvBG4AR1Sgc6BX3CQg==}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ ts-api-utils@2.4.0:
+ resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tsconfig-paths@3.15.0:
+ resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ tsx@4.21.0:
+ resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typed-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-length@1.0.3:
+ resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-offset@1.0.4:
+ resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-length@1.0.7:
+ resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
+ engines: {node: '>= 0.4'}
+
+ typed-query-selector@2.12.0:
+ resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==}
+
+ typescript-eslint@8.55.0:
+ resolution: {integrity: sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ unbox-primitive@1.1.0:
+ resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
+ engines: {node: '>= 0.4'}
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ undici@7.22.0:
+ resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==}
+ engines: {node: '>=20.18.1'}
+
+ unrs-resolver@1.11.1:
+ resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ vite@6.4.1:
+ resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ webdriver-bidi-protocol@0.4.1:
+ resolution: {integrity: sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+ deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ which-boxed-primitive@1.1.1:
+ resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
+ engines: {node: '>= 0.4'}
+
+ which-builtin-type@1.2.1:
+ resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
+ engines: {node: '>= 0.4'}
+
+ which-collection@1.0.2:
+ resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
+ engines: {node: '>= 0.4'}
+
+ which-typed-array@1.1.20:
+ resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==}
+ engines: {node: '>= 0.4'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ ws@8.19.0:
+ resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ 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.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yauzl@2.10.0:
+ resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zod-validation-error@4.0.2:
+ resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ zod: ^3.25.0 || ^4.0.0
+
+ zod@3.25.76:
+ resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+
+ zod@4.3.6:
+ resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
+
+snapshots:
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@babel/code-frame@7.29.0':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.29.0': {}
+
+ '@babel/core@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helpers': 7.28.6
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.29.1':
+ dependencies:
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.28.6':
+ dependencies:
+ '@babel/compat-data': 7.29.0
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.1
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.28.6':
+ dependencies:
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.6':
+ dependencies:
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+
+ '@babel/parser@7.29.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@babel/template@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+
+ '@babel/traverse@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@emnapi/core@1.8.1':
+ dependencies:
+ '@emnapi/wasi-threads': 1.1.0
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.8.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.1.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@esbuild/aix-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.27.3':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/android-arm@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm@0.27.3':
+ optional: true
+
+ '@esbuild/android-x64@0.25.12':
+ optional: true
+
+ '@esbuild/android-x64@0.27.3':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.12':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.3':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.12':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.3':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.3':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.12':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.3':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.12':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.3':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.12':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.3':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.3':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.12':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.3':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.12':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.3':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.12':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.3':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.12':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.3':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))':
+ dependencies:
+ eslint: 9.39.2(jiti@2.6.1)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.2': {}
+
+ '@eslint/config-array@0.21.1':
+ dependencies:
+ '@eslint/object-schema': 2.1.7
+ debug: 4.4.3
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.4.2':
+ dependencies:
+ '@eslint/core': 0.17.0
+
+ '@eslint/core@0.17.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.3':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.3
+ espree: 10.4.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.1
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.39.2': {}
+
+ '@eslint/object-schema@2.1.7': {}
+
+ '@eslint/plugin-kit@0.4.1':
+ dependencies:
+ '@eslint/core': 0.17.0
+ levn: 0.4.1
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.7':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@img/colour@1.0.0':
+ optional: true
+
+ '@img/sharp-darwin-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-riscv64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-riscv64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-riscv64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-s390x@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-wasm32@0.34.5':
+ dependencies:
+ '@emnapi/runtime': 1.8.1
+ optional: true
+
+ '@img/sharp-win32-arm64@0.34.5':
+ optional: true
+
+ '@img/sharp-win32-ia32@0.34.5':
+ optional: true
+
+ '@img/sharp-win32-x64@0.34.5':
+ optional: true
+
+ '@isaacs/cliui@9.0.0': {}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ dependencies:
+ '@emnapi/core': 1.8.1
+ '@emnapi/runtime': 1.8.1
+ '@tybys/wasm-util': 0.10.1
+ optional: true
+
+ '@next/env@16.1.1': {}
+
+ '@next/eslint-plugin-next@16.1.1':
+ dependencies:
+ fast-glob: 3.3.1
+
+ '@next/swc-darwin-arm64@16.1.1':
+ optional: true
+
+ '@next/swc-darwin-x64@16.1.1':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@16.1.1':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@16.1.1':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@16.1.1':
+ optional: true
+
+ '@next/swc-linux-x64-musl@16.1.1':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@16.1.1':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@16.1.1':
+ optional: true
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.20.1
+
+ '@nolyfill/is-core-module@1.0.39': {}
+
+ '@puppeteer/browsers@2.12.1':
+ dependencies:
+ debug: 4.4.3
+ extract-zip: 2.0.1
+ progress: 2.0.3
+ proxy-agent: 6.5.0
+ semver: 7.7.4
+ tar-fs: 3.1.1
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - bare-buffer
+ - react-native-b4a
+ - supports-color
+
+ '@rolldown/pluginutils@1.0.0-beta.27': {}
+
+ '@rollup/rollup-android-arm-eabi@4.57.1':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-openbsd-x64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.57.1':
+ optional: true
+
+ '@rtsao/scc@1.1.0': {}
+
+ '@swc/core-darwin-arm64@1.15.11':
+ optional: true
+
+ '@swc/core-darwin-x64@1.15.11':
+ optional: true
+
+ '@swc/core-linux-arm-gnueabihf@1.15.11':
+ optional: true
+
+ '@swc/core-linux-arm64-gnu@1.15.11':
+ optional: true
+
+ '@swc/core-linux-arm64-musl@1.15.11':
+ optional: true
+
+ '@swc/core-linux-x64-gnu@1.15.11':
+ optional: true
+
+ '@swc/core-linux-x64-musl@1.15.11':
+ optional: true
+
+ '@swc/core-win32-arm64-msvc@1.15.11':
+ optional: true
+
+ '@swc/core-win32-ia32-msvc@1.15.11':
+ optional: true
+
+ '@swc/core-win32-x64-msvc@1.15.11':
+ optional: true
+
+ '@swc/core@1.15.11':
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.25
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.15.11
+ '@swc/core-darwin-x64': 1.15.11
+ '@swc/core-linux-arm-gnueabihf': 1.15.11
+ '@swc/core-linux-arm64-gnu': 1.15.11
+ '@swc/core-linux-arm64-musl': 1.15.11
+ '@swc/core-linux-x64-gnu': 1.15.11
+ '@swc/core-linux-x64-musl': 1.15.11
+ '@swc/core-win32-arm64-msvc': 1.15.11
+ '@swc/core-win32-ia32-msvc': 1.15.11
+ '@swc/core-win32-x64-msvc': 1.15.11
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
+ '@swc/types@0.1.25':
+ dependencies:
+ '@swc/counter': 0.1.3
+
+ '@tailwindcss/node@4.1.18':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.19.0
+ jiti: 2.6.1
+ lightningcss: 1.30.2
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.18
+
+ '@tailwindcss/oxide-android-arm64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.18':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.18
+ '@tailwindcss/oxide-darwin-arm64': 4.1.18
+ '@tailwindcss/oxide-darwin-x64': 4.1.18
+ '@tailwindcss/oxide-freebsd-x64': 4.1.18
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.18
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.18
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.18
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.18
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.18
+
+ '@tailwindcss/postcss@4.1.18':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.1.18
+ '@tailwindcss/oxide': 4.1.18
+ postcss: 8.5.6
+ tailwindcss: 4.1.18
+
+ '@tootallnate/quickjs-emscripten@0.23.0': {}
+
+ '@tybys/wasm-util@0.10.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/chrome@0.0.315':
+ dependencies:
+ '@types/filesystem': 0.0.36
+ '@types/har-format': 1.2.16
+
+ '@types/estree@1.0.8': {}
+
+ '@types/filesystem@0.0.36':
+ dependencies:
+ '@types/filewriter': 0.0.33
+
+ '@types/filewriter@0.0.33': {}
+
+ '@types/har-format@1.2.16': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/json5@0.0.29': {}
+
+ '@types/node@22.19.11':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/react-dom@19.2.3(@types/react@19.2.14)':
+ dependencies:
+ '@types/react': 19.2.14
+
+ '@types/react@19.2.14':
+ dependencies:
+ csstype: 3.2.3
+
+ '@types/yauzl@2.10.3':
+ dependencies:
+ '@types/node': 22.19.11
+ optional: true
+
+ '@typescript-eslint/eslint-plugin@8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.2
+ '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.55.0
+ '@typescript-eslint/type-utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.55.0
+ eslint: 9.39.2(jiti@2.6.1)
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.4.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.55.0
+ '@typescript-eslint/types': 8.55.0
+ '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.55.0
+ debug: 4.4.3
+ eslint: 9.39.2(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.55.0(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.55.0
+ debug: 4.4.3
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.55.0':
+ dependencies:
+ '@typescript-eslint/types': 8.55.0
+ '@typescript-eslint/visitor-keys': 8.55.0
+
+ '@typescript-eslint/tsconfig-utils@8.55.0(typescript@5.9.3)':
+ dependencies:
+ typescript: 5.9.3
+
+ '@typescript-eslint/type-utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.55.0
+ '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ debug: 4.4.3
+ eslint: 9.39.2(jiti@2.6.1)
+ ts-api-utils: 2.4.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.55.0': {}
+
+ '@typescript-eslint/typescript-estree@8.55.0(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.55.0(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.55.0
+ '@typescript-eslint/visitor-keys': 8.55.0
+ debug: 4.4.3
+ minimatch: 9.0.5
+ semver: 7.7.4
+ tinyglobby: 0.2.15
+ ts-api-utils: 2.4.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
+ '@typescript-eslint/scope-manager': 8.55.0
+ '@typescript-eslint/types': 8.55.0
+ '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3)
+ eslint: 9.39.2(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.55.0':
+ dependencies:
+ '@typescript-eslint/types': 8.55.0
+ eslint-visitor-keys: 4.2.1
+
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-android-arm64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
+ dependencies:
+ '@napi-rs/wasm-runtime': 0.2.12
+ optional: true
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
+ optional: true
+
+ '@vitejs/plugin-react-swc@3.11.0(vite@6.4.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-beta.27
+ '@swc/core': 1.15.11
+ vite: 6.4.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+ transitivePeerDependencies:
+ - '@swc/helpers'
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ agent-base@7.1.4: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-regex@5.0.1: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ argparse@2.0.1: {}
+
+ aria-query@5.3.2: {}
+
+ array-buffer-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ is-array-buffer: 3.0.5
+
+ array-includes@3.1.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ is-string: 1.1.1
+ math-intrinsics: 1.1.0
+
+ array.prototype.findlast@1.2.5:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.findlastindex@1.2.6:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flat@1.3.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flatmap@1.3.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.tosorted@1.1.4:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-errors: 1.3.0
+ es-shim-unscopables: 1.1.0
+
+ arraybuffer.prototype.slice@1.0.4:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ is-array-buffer: 3.0.5
+
+ ast-types-flow@0.0.8: {}
+
+ ast-types@0.13.4:
+ dependencies:
+ tslib: 2.8.1
+
+ async-function@1.0.0: {}
+
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.1.0
+
+ axe-core@4.11.1: {}
+
+ axobject-query@4.1.0: {}
+
+ b4a@1.7.4: {}
+
+ balanced-match@1.0.2: {}
+
+ balanced-match@4.0.2:
+ dependencies:
+ jackspeak: 4.2.3
+
+ bare-events@2.8.2: {}
+
+ bare-fs@4.5.4:
+ dependencies:
+ bare-events: 2.8.2
+ bare-path: 3.0.0
+ bare-stream: 2.7.0(bare-events@2.8.2)
+ bare-url: 2.3.2
+ fast-fifo: 1.3.2
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - react-native-b4a
+ optional: true
+
+ bare-os@3.6.2:
+ optional: true
+
+ bare-path@3.0.0:
+ dependencies:
+ bare-os: 3.6.2
+ optional: true
+
+ bare-stream@2.7.0(bare-events@2.8.2):
+ dependencies:
+ streamx: 2.23.0
+ optionalDependencies:
+ bare-events: 2.8.2
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - react-native-b4a
+ optional: true
+
+ bare-url@2.3.2:
+ dependencies:
+ bare-path: 3.0.0
+ optional: true
+
+ baseline-browser-mapping@2.9.19: {}
+
+ basic-ftp@5.1.0: {}
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ brace-expansion@5.0.2:
+ dependencies:
+ balanced-match: 4.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.28.1:
+ dependencies:
+ baseline-browser-mapping: 2.9.19
+ caniuse-lite: 1.0.30001769
+ electron-to-chromium: 1.5.286
+ node-releases: 2.0.27
+ update-browserslist-db: 1.2.3(browserslist@4.28.1)
+
+ buffer-crc32@0.2.13: {}
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bind@1.0.8:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ callsites@3.1.0: {}
+
+ caniuse-lite@1.0.30001769: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ cheerio-select@2.1.0:
+ dependencies:
+ boolbase: 1.0.0
+ css-select: 5.2.2
+ css-what: 6.2.2
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+
+ cheerio@1.2.0:
+ dependencies:
+ cheerio-select: 2.1.0
+ dom-serializer: 2.0.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ encoding-sniffer: 0.2.1
+ htmlparser2: 10.1.0
+ parse5: 7.3.0
+ parse5-htmlparser2-tree-adapter: 7.1.0
+ parse5-parser-stream: 7.1.2
+ undici: 7.22.0
+ whatwg-mimetype: 4.0.0
+
+ chrome-launcher@1.2.1:
+ dependencies:
+ '@types/node': 22.19.11
+ escape-string-regexp: 4.0.0
+ is-wsl: 2.2.0
+ lighthouse-logger: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ chromium-bidi@14.0.0(devtools-protocol@0.0.1566079):
+ dependencies:
+ devtools-protocol: 0.0.1566079
+ mitt: 3.0.1
+ zod: 3.25.76
+
+ 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
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ concat-map@0.0.1: {}
+
+ convert-source-map@2.0.0: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css-select@5.2.2:
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 6.2.2
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ nth-check: 2.1.1
+
+ css-what@6.2.2: {}
+
+ csstype@3.2.3: {}
+
+ damerau-levenshtein@1.0.8: {}
+
+ data-uri-to-buffer@6.0.2: {}
+
+ data-view-buffer@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-offset@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ deep-is@0.1.4: {}
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ define-properties@1.2.1:
+ dependencies:
+ define-data-property: 1.1.4
+ has-property-descriptors: 1.0.2
+ object-keys: 1.1.1
+
+ degenerator@5.0.1:
+ dependencies:
+ ast-types: 0.13.4
+ escodegen: 2.1.0
+ esprima: 4.0.1
+
+ detect-libc@2.1.2: {}
+
+ devtools-protocol@0.0.1566079: {}
+
+ doctrine@2.1.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ domutils@3.2.2:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ electron-to-chromium@1.5.286: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ encoding-sniffer@0.2.1:
+ dependencies:
+ iconv-lite: 0.6.3
+ whatwg-encoding: 3.1.1
+
+ end-of-stream@1.4.5:
+ dependencies:
+ once: 1.4.0
+
+ enhanced-resolve@5.19.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.0
+
+ entities@4.5.0: {}
+
+ entities@6.0.1: {}
+
+ entities@7.0.1: {}
+
+ es-abstract@1.24.1:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ arraybuffer.prototype.slice: 1.0.4
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ data-view-buffer: 1.0.2
+ data-view-byte-length: 1.0.2
+ data-view-byte-offset: 1.0.1
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-set-tostringtag: 2.1.0
+ es-to-primitive: 1.3.0
+ function.prototype.name: 1.1.8
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ get-symbol-description: 1.1.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ internal-slot: 1.1.0
+ is-array-buffer: 3.0.5
+ is-callable: 1.2.7
+ is-data-view: 1.0.2
+ is-negative-zero: 2.0.3
+ is-regex: 1.2.1
+ is-set: 2.0.3
+ is-shared-array-buffer: 1.0.4
+ is-string: 1.1.1
+ is-typed-array: 1.1.15
+ is-weakref: 1.1.1
+ math-intrinsics: 1.1.0
+ object-inspect: 1.13.4
+ object-keys: 1.1.1
+ object.assign: 4.1.7
+ own-keys: 1.0.1
+ regexp.prototype.flags: 1.5.4
+ safe-array-concat: 1.1.3
+ safe-push-apply: 1.0.0
+ safe-regex-test: 1.1.0
+ set-proto: 1.0.0
+ stop-iteration-iterator: 1.1.0
+ string.prototype.trim: 1.2.10
+ string.prototype.trimend: 1.0.9
+ string.prototype.trimstart: 1.0.8
+ typed-array-buffer: 1.0.3
+ typed-array-byte-length: 1.0.3
+ typed-array-byte-offset: 1.0.4
+ typed-array-length: 1.0.7
+ unbox-primitive: 1.1.0
+ which-typed-array: 1.1.20
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-iterator-helpers@1.2.2:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-errors: 1.3.0
+ es-set-tostringtag: 2.1.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ internal-slot: 1.1.0
+ iterator.prototype: 1.1.5
+ safe-array-concat: 1.1.3
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es-shim-unscopables@1.1.0:
+ dependencies:
+ hasown: 2.0.2
+
+ es-to-primitive@1.3.0:
+ dependencies:
+ is-callable: 1.2.7
+ is-date-object: 1.1.0
+ is-symbol: 1.1.1
+
+ esbuild@0.25.12:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.12
+ '@esbuild/android-arm': 0.25.12
+ '@esbuild/android-arm64': 0.25.12
+ '@esbuild/android-x64': 0.25.12
+ '@esbuild/darwin-arm64': 0.25.12
+ '@esbuild/darwin-x64': 0.25.12
+ '@esbuild/freebsd-arm64': 0.25.12
+ '@esbuild/freebsd-x64': 0.25.12
+ '@esbuild/linux-arm': 0.25.12
+ '@esbuild/linux-arm64': 0.25.12
+ '@esbuild/linux-ia32': 0.25.12
+ '@esbuild/linux-loong64': 0.25.12
+ '@esbuild/linux-mips64el': 0.25.12
+ '@esbuild/linux-ppc64': 0.25.12
+ '@esbuild/linux-riscv64': 0.25.12
+ '@esbuild/linux-s390x': 0.25.12
+ '@esbuild/linux-x64': 0.25.12
+ '@esbuild/netbsd-arm64': 0.25.12
+ '@esbuild/netbsd-x64': 0.25.12
+ '@esbuild/openbsd-arm64': 0.25.12
+ '@esbuild/openbsd-x64': 0.25.12
+ '@esbuild/openharmony-arm64': 0.25.12
+ '@esbuild/sunos-x64': 0.25.12
+ '@esbuild/win32-arm64': 0.25.12
+ '@esbuild/win32-ia32': 0.25.12
+ '@esbuild/win32-x64': 0.25.12
+
+ esbuild@0.27.3:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.3
+ '@esbuild/android-arm': 0.27.3
+ '@esbuild/android-arm64': 0.27.3
+ '@esbuild/android-x64': 0.27.3
+ '@esbuild/darwin-arm64': 0.27.3
+ '@esbuild/darwin-x64': 0.27.3
+ '@esbuild/freebsd-arm64': 0.27.3
+ '@esbuild/freebsd-x64': 0.27.3
+ '@esbuild/linux-arm': 0.27.3
+ '@esbuild/linux-arm64': 0.27.3
+ '@esbuild/linux-ia32': 0.27.3
+ '@esbuild/linux-loong64': 0.27.3
+ '@esbuild/linux-mips64el': 0.27.3
+ '@esbuild/linux-ppc64': 0.27.3
+ '@esbuild/linux-riscv64': 0.27.3
+ '@esbuild/linux-s390x': 0.27.3
+ '@esbuild/linux-x64': 0.27.3
+ '@esbuild/netbsd-arm64': 0.27.3
+ '@esbuild/netbsd-x64': 0.27.3
+ '@esbuild/openbsd-arm64': 0.27.3
+ '@esbuild/openbsd-x64': 0.27.3
+ '@esbuild/openharmony-arm64': 0.27.3
+ '@esbuild/sunos-x64': 0.27.3
+ '@esbuild/win32-arm64': 0.27.3
+ '@esbuild/win32-ia32': 0.27.3
+ '@esbuild/win32-x64': 0.27.3
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escodegen@2.1.0:
+ dependencies:
+ esprima: 4.0.1
+ estraverse: 5.3.0
+ esutils: 2.0.3
+ optionalDependencies:
+ source-map: 0.6.1
+
+ eslint-config-next@16.1.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
+ dependencies:
+ '@next/eslint-plugin-next': 16.1.1
+ eslint: 9.39.2(jiti@2.6.1)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
+ globals: 16.4.0
+ typescript-eslint: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - '@typescript-eslint/parser'
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+
+ eslint-config-next@16.1.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
+ dependencies:
+ '@next/eslint-plugin-next': 16.1.1
+ eslint: 9.39.2(jiti@2.6.1)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
+ eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
+ globals: 16.4.0
+ typescript-eslint: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - '@typescript-eslint/parser'
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+
+ eslint-import-resolver-node@0.3.9:
+ dependencies:
+ debug: 3.2.7
+ is-core-module: 2.16.1
+ resolve: 1.22.11
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)):
+ dependencies:
+ '@nolyfill/is-core-module': 1.0.39
+ debug: 4.4.3
+ eslint: 9.39.2(jiti@2.6.1)
+ get-tsconfig: 4.13.6
+ is-bun-module: 2.0.0
+ stable-hash: 0.0.5
+ tinyglobby: 0.2.15
+ unrs-resolver: 1.11.1
+ optionalDependencies:
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
+ dependencies:
+ debug: 3.2.7
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 9.39.2(jiti@2.6.1)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.9
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 9.39.2(jiti@2.6.1)
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
+ hasown: 2.0.2
+ is-core-module: 2.16.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.1
+ semver: 6.3.1
+ string.prototype.trimend: 1.0.9
+ tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.9
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 9.39.2(jiti@2.6.1)
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
+ hasown: 2.0.2
+ is-core-module: 2.16.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.1
+ semver: 6.3.1
+ string.prototype.trimend: 1.0.9
+ tsconfig-paths: 3.15.0
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@2.6.1)):
+ dependencies:
+ aria-query: 5.3.2
+ array-includes: 3.1.9
+ array.prototype.flatmap: 1.3.3
+ ast-types-flow: 0.0.8
+ axe-core: 4.11.1
+ axobject-query: 4.1.0
+ damerau-levenshtein: 1.0.8
+ emoji-regex: 9.2.2
+ eslint: 9.39.2(jiti@2.6.1)
+ hasown: 2.0.2
+ jsx-ast-utils: 3.3.5
+ language-tags: 1.0.9
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ safe-regex-test: 1.1.0
+ string.prototype.includes: 2.0.1
+
+ eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@2.6.1)):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.0
+ eslint: 9.39.2(jiti@2.6.1)
+ hermes-parser: 0.25.1
+ zod: 4.3.6
+ zod-validation-error: 4.0.2(zod@4.3.6)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.6.1)):
+ dependencies:
+ array-includes: 3.1.9
+ array.prototype.findlast: 1.2.5
+ array.prototype.flatmap: 1.3.3
+ array.prototype.tosorted: 1.1.4
+ doctrine: 2.1.0
+ es-iterator-helpers: 1.2.2
+ eslint: 9.39.2(jiti@2.6.1)
+ estraverse: 5.3.0
+ hasown: 2.0.2
+ jsx-ast-utils: 3.3.5
+ minimatch: 3.1.2
+ object.entries: 1.1.9
+ object.fromentries: 2.0.8
+ object.values: 1.2.1
+ prop-types: 15.8.1
+ resolve: 2.0.0-next.5
+ semver: 6.3.1
+ string.prototype.matchall: 4.0.12
+ string.prototype.repeat: 1.0.0
+
+ eslint-scope@8.4.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.1: {}
+
+ eslint@9.39.2(jiti@2.6.1):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.21.1
+ '@eslint/config-helpers': 0.4.2
+ '@eslint/core': 0.17.0
+ '@eslint/eslintrc': 3.3.3
+ '@eslint/js': 9.39.2
+ '@eslint/plugin-kit': 0.4.1
+ '@humanfs/node': 0.16.7
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.8
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.6.1
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.4.0:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 4.2.1
+
+ esprima@4.0.1: {}
+
+ esquery@1.7.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ esutils@2.0.3: {}
+
+ events-universal@1.0.1:
+ dependencies:
+ bare-events: 2.8.2
+ transitivePeerDependencies:
+ - bare-abort-controller
+
+ extract-zip@2.0.1:
+ dependencies:
+ debug: 4.4.3
+ get-stream: 5.2.0
+ yauzl: 2.10.0
+ optionalDependencies:
+ '@types/yauzl': 2.10.3
+ transitivePeerDependencies:
+ - supports-color
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-fifo@1.3.2: {}
+
+ fast-glob@3.3.1:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.20.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fd-slicer@1.1.0:
+ dependencies:
+ pend: 1.2.0
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flatted@3.3.3: {}
+
+ for-each@0.3.5:
+ dependencies:
+ is-callable: 1.2.7
+
+ fsevents@2.3.2:
+ optional: true
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ function.prototype.name@1.1.8:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ functions-have-names: 1.2.3
+ hasown: 2.0.2
+ is-callable: 1.2.7
+
+ functions-have-names@1.2.3: {}
+
+ generator-function@2.0.1: {}
+
+ 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
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-stream@5.2.0:
+ dependencies:
+ pump: 3.0.3
+
+ get-symbol-description@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+
+ get-tsconfig@4.13.6:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ get-uri@6.0.5:
+ dependencies:
+ basic-ftp: 5.1.0
+ data-uri-to-buffer: 6.0.2
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@13.0.3:
+ dependencies:
+ minimatch: 10.2.0
+ minipass: 7.1.2
+ path-scurry: 2.0.1
+
+ globals@14.0.0: {}
+
+ globals@15.15.0: {}
+
+ globals@16.4.0: {}
+
+ globalthis@1.0.4:
+ dependencies:
+ define-properties: 1.2.1
+ gopd: 1.2.0
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ has-bigints@1.1.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+
+ has-proto@1.2.0:
+ dependencies:
+ dunder-proto: 1.0.1
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ hermes-estree@0.25.1: {}
+
+ hermes-parser@0.25.1:
+ dependencies:
+ hermes-estree: 0.25.1
+
+ htmlparser2@10.1.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ entities: 7.0.1
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ internal-slot@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ hasown: 2.0.2
+ side-channel: 1.1.0
+
+ ip-address@10.1.0: {}
+
+ is-array-buffer@3.0.5:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ is-async-function@2.1.1:
+ dependencies:
+ async-function: 1.0.0
+ call-bound: 1.0.4
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-bigint@1.1.0:
+ dependencies:
+ has-bigints: 1.1.0
+
+ is-boolean-object@1.2.2:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-bun-module@2.0.0:
+ dependencies:
+ semver: 7.7.4
+
+ is-callable@1.2.7: {}
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-data-view@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ is-typed-array: 1.1.15
+
+ is-date-object@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-docker@2.2.1: {}
+
+ is-extglob@2.1.1: {}
+
+ is-finalizationregistry@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-generator-function@1.1.2:
+ dependencies:
+ call-bound: 1.0.4
+ generator-function: 2.0.1
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-map@2.0.3: {}
+
+ is-negative-zero@2.0.3: {}
+
+ is-number-object@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-number@7.0.0: {}
+
+ is-regex@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ is-set@2.0.3: {}
+
+ is-shared-array-buffer@1.0.4:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-string@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-symbol@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-symbols: 1.1.0
+ safe-regex-test: 1.1.0
+
+ is-typed-array@1.1.15:
+ dependencies:
+ which-typed-array: 1.1.20
+
+ is-weakmap@2.0.2: {}
+
+ is-weakref@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-weakset@2.0.4:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ is-wsl@2.2.0:
+ dependencies:
+ is-docker: 2.2.1
+
+ isarray@2.0.5: {}
+
+ isexe@2.0.0: {}
+
+ iterator.prototype@1.1.5:
+ dependencies:
+ define-data-property: 1.1.4
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ has-symbols: 1.1.0
+ set-function-name: 2.0.2
+
+ jackspeak@4.2.3:
+ dependencies:
+ '@isaacs/cliui': 9.0.0
+
+ jiti@2.6.1: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@4.1.1:
+ dependencies:
+ argparse: 2.0.1
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@1.0.2:
+ dependencies:
+ minimist: 1.2.8
+
+ json5@2.2.3: {}
+
+ jsx-ast-utils@3.3.5:
+ dependencies:
+ array-includes: 3.1.9
+ array.prototype.flat: 1.3.3
+ object.assign: 4.1.7
+ object.values: 1.2.1
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ language-subtag-registry@0.3.23: {}
+
+ language-tags@1.0.9:
+ dependencies:
+ language-subtag-registry: 0.3.23
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lighthouse-logger@2.0.2:
+ dependencies:
+ debug: 4.4.3
+ marky: 1.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ lightningcss-android-arm64@1.30.2:
+ optional: true
+
+ lightningcss-darwin-arm64@1.30.2:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.2:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.2:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.2:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.2:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.2:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.2:
+ optional: true
+
+ lightningcss@1.30.2:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.30.2
+ lightningcss-darwin-arm64: 1.30.2
+ lightningcss-darwin-x64: 1.30.2
+ lightningcss-freebsd-x64: 1.30.2
+ lightningcss-linux-arm-gnueabihf: 1.30.2
+ lightningcss-linux-arm64-gnu: 1.30.2
+ lightningcss-linux-arm64-musl: 1.30.2
+ lightningcss-linux-x64-gnu: 1.30.2
+ lightningcss-linux-x64-musl: 1.30.2
+ lightningcss-win32-arm64-msvc: 1.30.2
+ lightningcss-win32-x64-msvc: 1.30.2
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.merge@4.6.2: {}
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ lru-cache@11.2.6: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lru-cache@7.18.3: {}
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ marky@1.3.0: {}
+
+ math-intrinsics@1.1.0: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ minimatch@10.2.0:
+ dependencies:
+ brace-expansion: 5.0.2
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimist@1.2.8: {}
+
+ minipass@7.1.2: {}
+
+ mitt@3.0.1: {}
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.11: {}
+
+ napi-postinstall@0.3.4: {}
+
+ natural-compare@1.4.0: {}
+
+ netmask@2.0.2: {}
+
+ next@16.1.1(@babel/core@7.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
+ dependencies:
+ '@next/env': 16.1.1
+ '@swc/helpers': 0.5.15
+ baseline-browser-mapping: 2.9.19
+ caniuse-lite: 1.0.30001769
+ postcss: 8.4.31
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.3)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 16.1.1
+ '@next/swc-darwin-x64': 16.1.1
+ '@next/swc-linux-arm64-gnu': 16.1.1
+ '@next/swc-linux-arm64-musl': 16.1.1
+ '@next/swc-linux-x64-gnu': 16.1.1
+ '@next/swc-linux-x64-musl': 16.1.1
+ '@next/swc-win32-arm64-msvc': 16.1.1
+ '@next/swc-win32-x64-msvc': 16.1.1
+ sharp: 0.34.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ node-releases@2.0.27: {}
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ object-assign@4.1.1: {}
+
+ object-inspect@1.13.4: {}
+
+ object-keys@1.1.1: {}
+
+ object.assign@4.1.7:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+ has-symbols: 1.1.0
+ object-keys: 1.1.1
+
+ object.entries@1.1.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ object.fromentries@2.0.8:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-object-atoms: 1.1.1
+
+ object.groupby@1.0.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+
+ object.values@1.2.1:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ own-keys@1.0.1:
+ dependencies:
+ get-intrinsic: 1.3.0
+ object-keys: 1.1.1
+ safe-push-apply: 1.0.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ pac-proxy-agent@7.2.0:
+ dependencies:
+ '@tootallnate/quickjs-emscripten': 0.23.0
+ agent-base: 7.1.4
+ debug: 4.4.3
+ get-uri: 6.0.5
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ pac-resolver: 7.0.1
+ socks-proxy-agent: 8.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ pac-resolver@7.0.1:
+ dependencies:
+ degenerator: 5.0.1
+ netmask: 2.0.2
+
+ package-json-from-dist@1.0.1: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse5-htmlparser2-tree-adapter@7.1.0:
+ dependencies:
+ domhandler: 5.0.3
+ parse5: 7.3.0
+
+ parse5-parser-stream@7.1.2:
+ dependencies:
+ parse5: 7.3.0
+
+ parse5@7.3.0:
+ dependencies:
+ entities: 6.0.1
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@2.0.1:
+ dependencies:
+ lru-cache: 11.2.6
+ minipass: 7.1.2
+
+ pend@1.2.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ playwright-core@1.58.2: {}
+
+ playwright@1.58.2:
+ dependencies:
+ playwright-core: 1.58.2
+ optionalDependencies:
+ fsevents: 2.3.2
+
+ possible-typed-array-names@1.1.0: {}
+
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier@3.8.1: {}
+
+ progress@2.0.3: {}
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ proxy-agent@6.5.0:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ lru-cache: 7.18.3
+ pac-proxy-agent: 7.2.0
+ proxy-from-env: 1.1.0
+ socks-proxy-agent: 8.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ proxy-from-env@1.1.0: {}
+
+ pump@3.0.3:
+ dependencies:
+ end-of-stream: 1.4.5
+ once: 1.4.0
+
+ punycode@2.3.1: {}
+
+ puppeteer-core@24.37.3:
+ dependencies:
+ '@puppeteer/browsers': 2.12.1
+ chromium-bidi: 14.0.0(devtools-protocol@0.0.1566079)
+ debug: 4.4.3
+ devtools-protocol: 0.0.1566079
+ typed-query-selector: 2.12.0
+ webdriver-bidi-protocol: 0.4.1
+ ws: 8.19.0
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - bare-buffer
+ - bufferutil
+ - react-native-b4a
+ - supports-color
+ - utf-8-validate
+
+ queue-microtask@1.2.3: {}
+
+ react-dom@19.2.3(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ scheduler: 0.27.0
+
+ react-is@16.13.1: {}
+
+ react@19.2.3: {}
+
+ reflect.getprototypeof@1.0.10:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ which-builtin-type: 1.2.1
+
+ regexp.prototype.flags@1.5.4:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-errors: 1.3.0
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ set-function-name: 2.0.2
+
+ require-directory@2.1.1: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve@1.22.11:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ resolve@2.0.0-next.5:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ reusify@1.1.0: {}
+
+ rimraf@6.1.2:
+ dependencies:
+ glob: 13.0.3
+ package-json-from-dist: 1.0.1
+
+ rollup@4.57.1:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.57.1
+ '@rollup/rollup-android-arm64': 4.57.1
+ '@rollup/rollup-darwin-arm64': 4.57.1
+ '@rollup/rollup-darwin-x64': 4.57.1
+ '@rollup/rollup-freebsd-arm64': 4.57.1
+ '@rollup/rollup-freebsd-x64': 4.57.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.57.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.57.1
+ '@rollup/rollup-linux-arm64-gnu': 4.57.1
+ '@rollup/rollup-linux-arm64-musl': 4.57.1
+ '@rollup/rollup-linux-loong64-gnu': 4.57.1
+ '@rollup/rollup-linux-loong64-musl': 4.57.1
+ '@rollup/rollup-linux-ppc64-gnu': 4.57.1
+ '@rollup/rollup-linux-ppc64-musl': 4.57.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.57.1
+ '@rollup/rollup-linux-riscv64-musl': 4.57.1
+ '@rollup/rollup-linux-s390x-gnu': 4.57.1
+ '@rollup/rollup-linux-x64-gnu': 4.57.1
+ '@rollup/rollup-linux-x64-musl': 4.57.1
+ '@rollup/rollup-openbsd-x64': 4.57.1
+ '@rollup/rollup-openharmony-arm64': 4.57.1
+ '@rollup/rollup-win32-arm64-msvc': 4.57.1
+ '@rollup/rollup-win32-ia32-msvc': 4.57.1
+ '@rollup/rollup-win32-x64-gnu': 4.57.1
+ '@rollup/rollup-win32-x64-msvc': 4.57.1
+ fsevents: 2.3.3
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safe-array-concat@1.1.3:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ has-symbols: 1.1.0
+ isarray: 2.0.5
+
+ safe-push-apply@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ isarray: 2.0.5
+
+ safe-regex-test@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-regex: 1.2.1
+
+ safer-buffer@2.1.2: {}
+
+ scheduler@0.27.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.4: {}
+
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ set-function-name@2.0.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.2
+
+ set-proto@1.0.0:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+
+ sharp@0.34.5:
+ dependencies:
+ '@img/colour': 1.0.0
+ detect-libc: 2.1.2
+ semver: 7.7.4
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.34.5
+ '@img/sharp-darwin-x64': 0.34.5
+ '@img/sharp-libvips-darwin-arm64': 1.2.4
+ '@img/sharp-libvips-darwin-x64': 1.2.4
+ '@img/sharp-libvips-linux-arm': 1.2.4
+ '@img/sharp-libvips-linux-arm64': 1.2.4
+ '@img/sharp-libvips-linux-ppc64': 1.2.4
+ '@img/sharp-libvips-linux-riscv64': 1.2.4
+ '@img/sharp-libvips-linux-s390x': 1.2.4
+ '@img/sharp-libvips-linux-x64': 1.2.4
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.4
+ '@img/sharp-linux-arm': 0.34.5
+ '@img/sharp-linux-arm64': 0.34.5
+ '@img/sharp-linux-ppc64': 0.34.5
+ '@img/sharp-linux-riscv64': 0.34.5
+ '@img/sharp-linux-s390x': 0.34.5
+ '@img/sharp-linux-x64': 0.34.5
+ '@img/sharp-linuxmusl-arm64': 0.34.5
+ '@img/sharp-linuxmusl-x64': 0.34.5
+ '@img/sharp-wasm32': 0.34.5
+ '@img/sharp-win32-arm64': 0.34.5
+ '@img/sharp-win32-ia32': 0.34.5
+ '@img/sharp-win32-x64': 0.34.5
+ optional: true
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ smart-buffer@4.2.0: {}
+
+ socks-proxy-agent@8.0.5:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ socks: 2.8.7
+ transitivePeerDependencies:
+ - supports-color
+
+ socks@2.8.7:
+ dependencies:
+ ip-address: 10.1.0
+ smart-buffer: 4.2.0
+
+ source-map-js@1.2.1: {}
+
+ source-map@0.6.1:
+ optional: true
+
+ stable-hash@0.0.5: {}
+
+ stop-iteration-iterator@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ internal-slot: 1.1.0
+
+ streamx@2.23.0:
+ dependencies:
+ events-universal: 1.0.1
+ fast-fifo: 1.3.2
+ text-decoder: 1.2.6
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - react-native-b4a
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string.prototype.includes@2.0.1:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+
+ string.prototype.matchall@4.0.12:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ internal-slot: 1.1.0
+ regexp.prototype.flags: 1.5.4
+ set-function-name: 2.0.2
+ side-channel: 1.1.0
+
+ string.prototype.repeat@1.0.0:
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+
+ string.prototype.trim@1.2.10:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-data-property: 1.1.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.1
+ es-object-atoms: 1.1.1
+ has-property-descriptors: 1.0.2
+
+ string.prototype.trimend@1.0.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ string.prototype.trimstart@1.0.8:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-bom@3.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.3):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.2.3
+ optionalDependencies:
+ '@babel/core': 7.29.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ tailwindcss@4.1.18: {}
+
+ tapable@2.3.0: {}
+
+ tar-fs@3.1.1:
+ dependencies:
+ pump: 3.0.3
+ tar-stream: 3.1.7
+ optionalDependencies:
+ bare-fs: 4.5.4
+ bare-path: 3.0.0
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - bare-buffer
+ - react-native-b4a
+
+ tar-stream@3.1.7:
+ dependencies:
+ b4a: 1.7.4
+ fast-fifo: 1.3.2
+ streamx: 2.23.0
+ transitivePeerDependencies:
+ - bare-abort-controller
+ - react-native-b4a
+
+ text-decoder@1.2.6:
+ dependencies:
+ b4a: 1.7.4
+ transitivePeerDependencies:
+ - react-native-b4a
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ ts-api-utils@2.4.0(typescript@5.9.3):
+ dependencies:
+ typescript: 5.9.3
+
+ tsconfig-paths@3.15.0:
+ dependencies:
+ '@types/json5': 0.0.29
+ json5: 1.0.2
+ minimist: 1.2.8
+ strip-bom: 3.0.0
+
+ tslib@2.8.1: {}
+
+ tsx@4.21.0:
+ dependencies:
+ esbuild: 0.27.3
+ get-tsconfig: 4.13.6
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typed-array-buffer@1.0.3:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-length@1.0.3:
+ dependencies:
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-offset@1.0.4:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+ reflect.getprototypeof: 1.0.10
+
+ typed-array-length@1.0.7:
+ dependencies:
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ is-typed-array: 1.1.15
+ possible-typed-array-names: 1.1.0
+ reflect.getprototypeof: 1.0.10
+
+ typed-query-selector@2.12.0: {}
+
+ typescript-eslint@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 9.39.2(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@5.9.3: {}
+
+ unbox-primitive@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-bigints: 1.1.0
+ has-symbols: 1.1.0
+ which-boxed-primitive: 1.1.1
+
+ undici-types@6.21.0: {}
+
+ undici@7.22.0: {}
+
+ unrs-resolver@1.11.1:
+ dependencies:
+ napi-postinstall: 0.3.4
+ optionalDependencies:
+ '@unrs/resolver-binding-android-arm-eabi': 1.11.1
+ '@unrs/resolver-binding-android-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-x64': 1.11.1
+ '@unrs/resolver-binding-freebsd-x64': 1.11.1
+ '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-musl': 1.11.1
+ '@unrs/resolver-binding-wasm32-wasi': 1.11.1
+ '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-x64-msvc': 1.11.1
+
+ update-browserslist-db@1.2.3(browserslist@4.28.1):
+ dependencies:
+ browserslist: 4.28.1
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ vite@6.4.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0):
+ dependencies:
+ esbuild: 0.25.12
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.57.1
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ '@types/node': 22.19.11
+ fsevents: 2.3.3
+ jiti: 2.6.1
+ lightningcss: 1.30.2
+ tsx: 4.21.0
+
+ webdriver-bidi-protocol@0.4.1: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ which-boxed-primitive@1.1.1:
+ dependencies:
+ is-bigint: 1.1.0
+ is-boolean-object: 1.2.2
+ is-number-object: 1.1.1
+ is-string: 1.1.1
+ is-symbol: 1.1.1
+
+ which-builtin-type@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ function.prototype.name: 1.1.8
+ has-tostringtag: 1.0.2
+ is-async-function: 2.1.1
+ is-date-object: 1.1.0
+ is-finalizationregistry: 1.1.1
+ is-generator-function: 1.1.2
+ is-regex: 1.2.1
+ is-weakref: 1.1.1
+ isarray: 2.0.5
+ which-boxed-primitive: 1.1.1
+ which-collection: 1.0.2
+ which-typed-array: 1.1.20
+
+ which-collection@1.0.2:
+ dependencies:
+ is-map: 2.0.3
+ is-set: 2.0.3
+ is-weakmap: 2.0.2
+ is-weakset: 2.0.4
+
+ which-typed-array@1.1.20:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ for-each: 0.3.5
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrappy@1.0.2: {}
+
+ ws@8.19.0: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ 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
+
+ yauzl@2.10.0:
+ dependencies:
+ buffer-crc32: 0.2.13
+ fd-slicer: 1.1.0
+
+ yocto-queue@0.1.0: {}
+
+ zod-validation-error@4.0.2(zod@4.3.6):
+ dependencies:
+ zod: 4.3.6
+
+ zod@3.25.76: {}
+
+ zod@4.3.6: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..4a75af2
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,4 @@
+packages:
+ - "apps/*"
+ - "apps/client/*"
+ - "packages/*"
\ No newline at end of file
diff --git a/scripts/demo-verify.ts b/scripts/demo-verify.ts
new file mode 100644
index 0000000..d2cc8fe
--- /dev/null
+++ b/scripts/demo-verify.ts
@@ -0,0 +1,313 @@
+import { spawn, ChildProcess } from "node:child_process";
+import process from "node:process";
+import { launch as launchChrome } from "chrome-launcher";
+import puppeteer, { type Browser } from "puppeteer-core";
+import {
+ buildSurfaceUrlsFromPlan,
+ type GbvPageType,
+ type GbvVerificationArtifact,
+} from "@gbv/core";
+import { gbvConfig } from "@gbv/config";
+
+type InitResponse = {
+ ok: boolean;
+ sessionId: string;
+ artifact: GbvVerificationArtifact;
+ pagePlan: GbvPageType[];
+ pageNonces: string[];
+ nonceLeafId: string;
+};
+
+type CatalogResponse = {
+ ok: boolean;
+ courses: GbvVerificationArtifact[];
+};
+
+type Snapshot = {
+ url: string;
+ html: string;
+ injectedNonce: string;
+};
+
+function assertCondition(condition: unknown, message: string): void {
+ if (!condition) {
+ throw new Error(message);
+ }
+}
+
+function spawnProcess(args: string[]): ChildProcess {
+ return spawn("corepack", ["pnpm", ...args], {
+ stdio: "inherit",
+ shell: process.platform === "win32",
+ });
+}
+
+function terminateProcess(child: ChildProcess): void {
+ if (!child.pid) return;
+
+ if (process.platform === "win32") {
+ spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
+ stdio: "ignore",
+ shell: true,
+ });
+ return;
+ }
+
+ child.kill("SIGTERM");
+}
+
+async function waitForReady(url: string, timeoutMs = 90_000): Promise {
+ const started = Date.now();
+
+ while (Date.now() - started < timeoutMs) {
+ try {
+ const response = await fetch(url);
+ if (response.ok) return;
+ } catch {
+ // ignored while booting
+ }
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ }
+
+ throw new Error(`Timed out waiting for ${url}`);
+}
+
+async function launchHeadlessBrowser(): Promise<{ browser: Browser; stop: () => Promise }> {
+ const chrome = await launchChrome({
+ chromeFlags: [
+ "--headless",
+ "--disable-gpu",
+ "--no-first-run",
+ "--no-default-browser-check",
+ ],
+ logLevel: "error",
+ });
+
+ const browser = await puppeteer.connect({
+ browserURL: `http://127.0.0.1:${chrome.port}`,
+ defaultViewport: { width: 1280, height: 800 },
+ });
+
+ return {
+ browser,
+ stop: async () => {
+ await browser.close();
+ await chrome.kill();
+ },
+ };
+}
+
+async function captureSnapshotInBrowser(
+ browser: Browser,
+ url: string,
+ nonceLeafId: string,
+ nonce: string,
+): Promise {
+ const page = await browser.newPage();
+
+ try {
+ await page.goto(url, {
+ waitUntil: "domcontentloaded",
+ timeout: 30_000,
+ });
+
+ const snapshot = await page.evaluate(
+ ({ nonceLeafIdArg, nonceValue }) => {
+ const selector = `meta[${nonceLeafIdArg}]`;
+ let nonceNode = document.querySelector(selector) as HTMLMetaElement | null;
+
+ if (!nonceNode) {
+ nonceNode = document.createElement("meta");
+ nonceNode.setAttribute(nonceLeafIdArg, nonceValue);
+ (document.head || document.documentElement).appendChild(nonceNode);
+ } else {
+ nonceNode.setAttribute(nonceLeafIdArg, nonceValue);
+ }
+
+ return {
+ url: window.location.href,
+ html: document.documentElement?.outerHTML || "",
+ injectedNonce: nonceNode.getAttribute(nonceLeafIdArg) || "",
+ };
+ },
+ {
+ nonceLeafIdArg: nonceLeafId,
+ nonceValue: nonce,
+ },
+ );
+
+ if (!snapshot.url || !snapshot.html || !snapshot.injectedNonce) {
+ throw new Error(`Invalid surface snapshot for ${url}`);
+ }
+
+ return snapshot;
+ } finally {
+ await page.close();
+ }
+}
+
+async function fetchCourseCatalog(): Promise {
+ const response = await fetch(
+ `${gbvConfig.origins.syntheticClient}${gbvConfig.demo.courseCatalogApiPath}`,
+ );
+ const json = (await response.json()) as CatalogResponse;
+ if (!response.ok || !json.ok || !Array.isArray(json.courses)) {
+ throw new Error(`Failed to load synthetic course catalog: ${JSON.stringify(json)}`);
+ }
+ return json.courses;
+}
+
+async function runCourseVerification(
+ browser: Browser,
+ artifact: GbvVerificationArtifact,
+): Promise<{ artifact: GbvVerificationArtifact; status: number; body: any }> {
+ const initResponse = await fetch(`${gbvConfig.origins.server}/api/gbv/init`, {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify({ artifact }),
+ });
+
+ const initJson = (await initResponse.json()) as InitResponse;
+ if (!initResponse.ok || !initJson.ok) {
+ throw new Error(`Init failed for ${artifact.publicCourseKey}: ${JSON.stringify(initJson)}`);
+ }
+
+ const urls = buildSurfaceUrlsFromPlan({
+ origin: gbvConfig.origins.syntheticClient,
+ pagePlan: initJson.pagePlan,
+ templates: gbvConfig.demo.surfacePathTemplates,
+ artifact,
+ });
+
+ const pages = [];
+ for (let i = 0; i < urls.length; i += 1) {
+ const url = urls[i];
+ const nonce = initJson.pageNonces[i];
+ const snapshot = await captureSnapshotInBrowser(browser, url, initJson.nonceLeafId, nonce);
+
+ pages.push({
+ url: snapshot.url,
+ injectedNonce: snapshot.injectedNonce,
+ html: snapshot.html,
+ });
+ }
+
+ const submitResponse = await fetch(`${gbvConfig.origins.server}/api/gbv/submit`, {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify({
+ sessionId: initJson.sessionId,
+ courseId: artifact.courseId,
+ provider: gbvConfig.protocol.providerId,
+ pages,
+ }),
+ });
+
+ const submitJson = await submitResponse.json();
+ return {
+ artifact,
+ status: submitResponse.status,
+ body: submitJson,
+ };
+}
+
+function evaluateResult(result: { artifact: GbvVerificationArtifact; status: number; body: any }): void {
+ const key = result.artifact.publicCourseKey;
+ const body = result.body as Record;
+
+ if (key === gbvConfig.demo.verifierBaselineCourseKey) {
+ assertCondition(result.status === 200, `Expected baseline status 200 for ${key}`);
+ assertCondition(body.ok === true, `Expected baseline ok=true for ${key}`);
+ assertCondition(body.accepted === true, `Expected baseline accepted=true for ${key}`);
+ assertCondition(
+ body?.verificationLog?.structure?.ok === true,
+ `Expected baseline structure.ok=true for ${key}`,
+ );
+ assertCondition(
+ body?.verificationLog?.semantic?.ok === true,
+ `Expected baseline semantic.ok=true for ${key}`,
+ );
+ console.log(`${key}: ACCEPTED (strict checks passed)`);
+ return;
+ }
+
+ if (key === gbvConfig.demo.verifierAdversarialCourseKey) {
+ assertCondition(result.status === 400, `Expected adversarial status 400 for ${key}`);
+ assertCondition(body.ok === false, `Expected adversarial ok=false for ${key}`);
+ assertCondition(
+ body.code === "SEMANTIC_VERIFICATION_FAILED",
+ `Expected adversarial code=SEMANTIC_VERIFICATION_FAILED for ${key}`,
+ );
+ const failedInvariantIds: string[] =
+ body?.meta?.failedInvariantIds || body?.failedInvariantIds || [];
+ assertCondition(
+ failedInvariantIds.includes("grade_threshold"),
+ `Expected adversarial failure to include grade_threshold for ${key}`,
+ );
+ assertCondition(
+ failedInvariantIds.includes("course_key_consistency"),
+ `Expected adversarial failure to include course_key_consistency for ${key}`,
+ );
+ console.log(`${key}: REJECTED (strict checks passed)`);
+ return;
+ }
+
+ throw new Error(`Unexpected verifier key in strict evaluation: ${key}`);
+}
+
+async function main() {
+ const server = spawnProcess(["--filter", "@gbv/server", "dev"]);
+ const client = spawnProcess(["--filter", "@gbv/synthetic-client", "dev"]);
+
+ const cleanupProcesses = () => {
+ terminateProcess(server);
+ terminateProcess(client);
+ };
+
+ process.on("SIGINT", cleanupProcesses);
+ process.on("SIGTERM", cleanupProcesses);
+
+ let stopBrowser: (() => Promise) | null = null;
+
+ try {
+ await waitForReady(`${gbvConfig.origins.server}/api/health`);
+ await waitForReady(`${gbvConfig.origins.syntheticClient}/hub`);
+
+ const browserRuntime = await launchHeadlessBrowser();
+ stopBrowser = browserRuntime.stop;
+
+ const catalog = await fetchCourseCatalog();
+ const selectedKeys = new Set([
+ gbvConfig.demo.verifierBaselineCourseKey,
+ gbvConfig.demo.verifierAdversarialCourseKey,
+ ]);
+ const selected = catalog.filter((course) => selectedKeys.has(course.publicCourseKey));
+
+ assertCondition(
+ selected.length === selectedKeys.size,
+ `Expected ${selectedKeys.size} verifier courses, found ${selected.length}`,
+ );
+
+ const results = [];
+ for (const artifact of selected) {
+ const result = await runCourseVerification(browserRuntime.browser, artifact);
+ results.push(result);
+ }
+
+ for (const result of results) {
+ evaluateResult(result);
+ }
+
+ console.log("OVERALL: PASS (strict assertions)");
+ } finally {
+ if (stopBrowser) {
+ await stopBrowser();
+ }
+ cleanupProcesses();
+ }
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/scripts/dev-orchestrator.ts b/scripts/dev-orchestrator.ts
new file mode 100644
index 0000000..5fdcc13
--- /dev/null
+++ b/scripts/dev-orchestrator.ts
@@ -0,0 +1,69 @@
+import { spawn, type ChildProcess } from "node:child_process";
+import { resolve } from "node:path";
+import process from "node:process";
+import { gbvConfig } from "@gbv/config";
+
+function run(command: string, args: string[], options: { wait: true }): Promise;
+function run(command: string, args: string[], options?: { wait?: false }): ChildProcess;
+function run(command: string, args: string[], options: { wait?: boolean } = {}) {
+ const child = spawn(command, args, {
+ stdio: "inherit",
+ shell: process.platform === "win32",
+ });
+
+ if (options.wait) {
+ return new Promise((resolveCode) => {
+ child.on("exit", (code) => resolveCode(code ?? 0));
+ });
+ }
+
+ return child;
+}
+
+function terminate(pid?: number): void {
+ if (!pid) return;
+
+ if (process.platform === "win32") {
+ spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {
+ stdio: "ignore",
+ shell: true,
+ });
+ return;
+ }
+
+ process.kill(pid, "SIGTERM");
+}
+
+async function main() {
+ const pnpm = "corepack";
+ const pnpmArgs = ["pnpm"];
+ console.log("[dev] building extension artifacts...");
+ const buildExit = await run(pnpm, [...pnpmArgs, "--filter", "@gbv/extension", "build"], {
+ wait: true,
+ });
+ if (buildExit !== 0) {
+ process.exit(buildExit);
+ }
+
+ console.log(`[dev] extension unpacked directory: ${resolve(gbvConfig.extension.buildDir)}`);
+ console.log(`[dev] synthetic hub URL: ${gbvConfig.origins.syntheticClient}/hub`);
+ console.log(
+ `[dev] synthetic course catalog API: ${gbvConfig.origins.syntheticClient}${gbvConfig.demo.courseCatalogApiPath}`,
+ );
+
+ const server = run(pnpm, [...pnpmArgs, "--filter", "@gbv/server", "dev"]);
+ const client = run(pnpm, [...pnpmArgs, "--filter", "@gbv/synthetic-client", "dev"]);
+
+ const shutdown = () => {
+ terminate(server.pid);
+ terminate(client.pid);
+ };
+
+ process.on("SIGINT", shutdown);
+ process.on("SIGTERM", shutdown);
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/scripts/extension-smoke.ts b/scripts/extension-smoke.ts
new file mode 100644
index 0000000..0afc595
--- /dev/null
+++ b/scripts/extension-smoke.ts
@@ -0,0 +1,91 @@
+import assert from "node:assert/strict";
+import { access } from "node:fs/promises";
+import path from "node:path";
+import process from "node:process";
+import { chromium } from "playwright";
+
+async function ensureBuildExists(buildPath: string): Promise {
+ await access(path.join(buildPath, "manifest.json"));
+ await access(path.join(buildPath, "background.js"));
+}
+
+async function main(): Promise {
+ const extensionPath = path.resolve("apps/extension/build");
+ await ensureBuildExists(extensionPath);
+
+ const userDataDir = path.resolve(".tmp-extension-smoke-profile");
+ const context = await chromium.launchPersistentContext(userDataDir, {
+ headless: false,
+ args: [
+ `--disable-extensions-except=${extensionPath}`,
+ `--load-extension=${extensionPath}`,
+ "--no-first-run",
+ "--no-default-browser-check",
+ ],
+ });
+
+ try {
+ let worker = context.serviceWorkers()[0];
+ if (!worker) {
+ worker = await context.waitForEvent("serviceworker", { timeout: 15_000 });
+ }
+ assert.ok(worker, "extension service worker failed to start");
+
+ const workerUrl = worker.url();
+ assert.ok(workerUrl.startsWith("chrome-extension://"), "unexpected service worker URL");
+ const extensionId = new URL(workerUrl).host;
+ const extensionPage = await context.newPage();
+ await extensionPage.goto(`chrome-extension://${extensionId}/index.html`);
+
+ let payload: {
+ response?: { ok?: boolean; serviceWorker?: string };
+ lastError?: string | null;
+ } | null = null;
+ for (let attempt = 1; attempt <= 20; attempt += 1) {
+ const ping = await extensionPage.evaluate(async () => {
+ type RuntimeResponse = { ok?: boolean; serviceWorker?: string };
+ type ChromeRuntime = {
+ runtime: {
+ sendMessage: (
+ message: { type: string },
+ callback: (response: RuntimeResponse | undefined) => void,
+ ) => void;
+ lastError?: { message?: string };
+ };
+ };
+ const { chrome } = globalThis as typeof globalThis & { chrome: ChromeRuntime };
+ return await new Promise((resolve) => {
+ chrome.runtime.sendMessage({ type: "GBV_PING" }, (response) => {
+ resolve({
+ response,
+ lastError: chrome.runtime.lastError?.message || null,
+ });
+ });
+ });
+ });
+ payload = ping as {
+ response?: { ok?: boolean; serviceWorker?: string };
+ lastError?: string | null;
+ };
+ if (!payload.lastError && payload.response?.ok) {
+ break;
+ }
+ await new Promise((resolve) => setTimeout(resolve, 250));
+ }
+
+ assert.ok(payload, "missing ping payload");
+ assert.equal(payload.lastError, null, `GBV_PING failed: ${payload.lastError}`);
+ assert.equal(payload.response?.ok, true, "GBV_PING did not return ok=true");
+ assert.equal(payload.response?.serviceWorker, "ready", "GBV_PING did not confirm readiness");
+ await extensionPage.close();
+
+ console.log("Extension smoke: PASS");
+ } finally {
+ await context.close();
+ }
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/scripts/generate-extension-manifest.ts b/scripts/generate-extension-manifest.ts
new file mode 100644
index 0000000..846a81d
--- /dev/null
+++ b/scripts/generate-extension-manifest.ts
@@ -0,0 +1,34 @@
+import { readFile, writeFile } from "node:fs/promises";
+import { fileURLToPath } from "node:url";
+import { gbvConfig } from "@gbv/config";
+
+const templatePath = fileURLToPath(
+ new URL("../apps/extension/public/manifest.template.json", import.meta.url),
+);
+const outputPath = fileURLToPath(new URL("../apps/extension/public/manifest.json", import.meta.url));
+
+async function main() {
+ const template = await readFile(templatePath, "utf8");
+ const compiled = template
+ .replace(/"__GBV_EXTENSION_NAME__"/g, JSON.stringify(gbvConfig.extension.name))
+ .replace(
+ /"__GBV_EXTENSION_VERSION__"/g,
+ JSON.stringify(gbvConfig.extension.version),
+ )
+ .replace(
+ /__GBV_PERMISSIONS__/g,
+ JSON.stringify(gbvConfig.extension.permissions, null, 2),
+ )
+ .replace(
+ /__GBV_HOST_PERMISSIONS__/g,
+ JSON.stringify(gbvConfig.extension.hostPermissions, null, 2),
+ );
+
+ await writeFile(outputPath, compiled);
+ console.log(`Generated extension manifest at ${outputPath}`);
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/scripts/setup.sh b/scripts/setup.sh
deleted file mode 100644
index a9bf588..0000000
--- a/scripts/setup.sh
+++ /dev/null
@@ -1 +0,0 @@
-#!/bin/bash
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 0000000..c774708
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "lib": ["ES2022", "DOM"],
+ "strict": true,
+ "noEmit": true,
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "baseUrl": "."
+ }
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..55e1edc
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "./tsconfig.base.json",
+ "compilerOptions": {
+ "paths": {
+ "@gbv/config": ["packages/gbv-config/src/index.ts"],
+ "@gbv/core": ["packages/gbv-core/src/index.ts"],
+ "@gbv/core/browser": ["packages/gbv-core/src/browser.ts"]
+ }
+ },
+ "include": ["scripts/**/*.ts", "gbv.config.ts"],
+ "exclude": ["node_modules"]
+}