diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..f2aa093
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,5 @@
+# Required: create a key at https://platform.claude.com/settings/keys
+ANTHROPIC_API_KEY=your_anthropic_api_key
+
+# Optional: pin a different compatible Claude model.
+ANTHROPIC_MODEL=claude-sonnet-5
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..fac8dd6
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,39 @@
+name: CI
+
+on:
+ pull_request:
+ push:
+ branches: [master]
+
+permissions:
+ contents: read
+
+jobs:
+ verify:
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Lint
+ run: npm run lint
+
+ - name: Type-check
+ run: npm run typecheck
+
+ - name: Test
+ run: npm test
+
+ - name: Build
+ run: npm run build
diff --git a/.gitignore b/.gitignore
index 9c8a03f..d0305fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,9 +32,9 @@ yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
+!.env.example
# vercel
-.vercel
# typescript
*.tsbuildinfo
diff --git a/CLAUDE.md b/CLAUDE.md
index 6440cee..64ec2fc 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -15,8 +15,9 @@ ClearPath SUD is a web-based tool for substance use disorder (SUD) professionals
## Domain Context
- **ASAM** (American Society of Addiction Medicine) defines a multidimensional assessment framework with 6 dimensions used for SUD treatment planning
+- The ASAM Mapper targets the adult **Fourth Edition** dimensions; readiness to change is integrated across dimensions rather than treated as a standalone dimension
- **SUDP** = Substance Use Disorder Professional
-- Clinical documentation must align with ASAM criteria for insurance reimbursement and regulatory compliance
+- Clinical documentation requirements vary by payer and jurisdiction; ASAM alignment may be one relevant requirement but does not itself guarantee compliance
- The target users are trainees and early-career clinicians who need guidance writing assessments, not experienced supervisors
- Think "Goblin Tools for SUD documentation" — simple, focused utilities, not an EHR replacement
@@ -24,7 +25,7 @@ ClearPath SUD is a web-based tool for substance use disorder (SUD) professionals
- **Next.js 16** (App Router, TypeScript, `src/` directory)
- **Tailwind CSS v4** — clinical palette (blues/whites/slates)
-- **Anthropic SDK** (`@anthropic-ai/sdk`) — Claude Sonnet for AI generation
+- **Anthropic SDK** (`@anthropic-ai/sdk`) — Claude Sonnet 5 by default, configurable with `ANTHROPIC_MODEL`
- **Streaming responses** — text appears progressively
## Development
@@ -36,7 +37,7 @@ npm run start # Run production build
npm run lint # Run ESLint
```
-Requires `ANTHROPIC_API_KEY` in `.env.local`.
+Requires `ANTHROPIC_API_KEY` in `.env.local`. Copy `.env.example` to get started.
## Project Structure
@@ -50,6 +51,7 @@ src/
│ ├── doc-reviewer/ # Tool 3: Documentation Reviewer
│ └── api/ # Streaming API routes for each tool
├── components/ # Shared UI components
+├── hooks/ # Shared client-side streaming behavior
│ ├── nav.tsx # Top navigation
│ ├── tool-card.tsx # Home page cards
│ ├── text-input.tsx # Textarea with char count
@@ -57,16 +59,25 @@ src/
│ ├── result-display.tsx # Streaming output + copy button
│ └── dimension-badge.tsx # ASAM dimension status badges
└── lib/
- ├── anthropic.ts # Anthropic client singleton
- └── prompts.ts # System prompts for all 3 tools
+ ├── anthropic.ts # Lazy server-only Anthropic client
+ ├── prompts.ts # System prompts for all 3 tools
+ ├── streaming-tool-route.ts # Shared validation and streaming response
+ └── tool-input.ts # Shared input limits and validation
```
## Architecture
-- **No database.** Text goes to Claude API and back; nothing persists.
+- **No application database.** Text is processed by the server and Anthropic API; hosting/provider logs and retention settings still matter.
- **No auth.** MVP is open; auth comes later.
-- **Each tool is self-contained.** Page owns its own state (input, result, isLoading).
-- **All API routes follow the same pattern:** validate → call Claude with system prompt → stream back.
+- **Each page is thin.** It owns the input text while the shared streaming hook owns result, error, loading, and cancellation state.
+- **Shared request path:** thin API adapters call one validator/streaming helper; pages use one client streaming hook.
+
+## Safety boundaries
+
+- Treat the project as an educational prototype, not clinical decision support.
+- Do not add claims that it diagnoses, assigns ASAM risk ratings, recommends level of care, or guarantees compliance.
+- Keep the visible de-identification and clinician-review warnings intact.
+- Clinical prompt changes should be checked against current primary sources and reviewed by qualified clinicians before real-world use.
## Status
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a3678b5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,202 @@
+# ClearPath SUD
+
+ClearPath is a focused set of AI-assisted drafting tools for substance use disorder (SUD) professionals. It helps clinicians and trainees organize treatment-plan language, check whether an assessment addresses the six dimensions in *The ASAM Criteria, Fourth Edition*, and review clinical documentation before submission.
+
+> [!IMPORTANT]
+> ClearPath is an educational prototype, not a clinical decision-support system or an electronic health record. It does not diagnose, assign ASAM risk ratings, determine level of care, replace qualified supervision, or guarantee clinical, payer, legal, or regulatory compliance. Do not enter identifiable patient information into an unapproved deployment.
+
+## What it does
+
+| Tool | Input | Output |
+| --- | --- | --- |
+| Treatment Plan Helper | De-identified client statements and clinical observations | A problem statement, long-term goal, SMART objectives, interventions, and clinical rationale |
+| ASAM Mapper | A de-identified assessment narrative | A documentation-coverage review for all six Fourth Edition dimensions, including evidence and suggested follow-up areas |
+| Documentation Reviewer | A de-identified progress note, assessment, or treatment-plan update | Supervisor-style feedback on strengths, clarity, person-first language, completeness, and revision priorities |
+
+Responses stream into the browser as they are generated. The application itself has no database, user accounts, or persistence layer.
+
+## Clinical framework
+
+The ASAM Mapper follows the adult assessment dimensions in *The ASAM Criteria, Fourth Edition*:
+
+1. Intoxication, Withdrawal, and Addiction Medications
+2. Biomedical Conditions
+3. Psychiatric and Cognitive Conditions
+4. Substance Use-Related Risks
+5. Recovery Environment Interactions
+6. Person-Centered Considerations
+
+Readiness and motivation are considered across the Fourth Edition dimensions and during treatment planning; they are not represented as a standalone dimension. See ASAM's [Fourth Edition overview](https://www.asam.org/asam-criteria/asam-criteria-4th-edition) and [official assessment guides](https://www.asam.org/asam-criteria/implementation-tools/Criteria-assessment-guides) for authoritative guidance.
+
+ClearPath is not affiliated with, endorsed by, or certified by the American Society of Addiction Medicine. “ASAM” and “The ASAM Criteria” are the property of their respective owner. This repository implements a documentation aid and does not reproduce the proprietary admission criteria or replace the official materials and training.
+
+## How it works
+
+```mermaid
+flowchart LR
+ A[Browser] -->|de-identified text| B[Next.js API route]
+ B --> C[Shared validation]
+ C -->|50–5,000 characters| D[Anthropic Messages API]
+ D -->|text stream| B
+ B -->|no-store response| A
+```
+
+Each tool has its own system prompt and token budget, but the server-side request handling is shared. Requests are validated again on the server, sent to the configured Claude model, and returned as plain-text streams. Navigating away or pressing **Stop** aborts the browser request.
+
+## Tech stack
+
+- [Next.js](https://nextjs.org/) App Router with TypeScript
+- React
+- Tailwind CSS
+- [Anthropic TypeScript SDK](https://github.com/anthropics/anthropic-sdk-typescript)
+- Vitest for unit tests
+- GitHub Actions for lint, type, test, and build verification
+
+The default model is `claude-sonnet-5`; set `ANTHROPIC_MODEL` to pin another compatible Claude API model without changing code.
+
+## Quick start
+
+### Prerequisites
+
+- Node.js 20.9 or newer (Node.js 22 LTS is used in CI)
+- npm
+- An [Anthropic API key](https://platform.claude.com/settings/keys)
+
+### Install and run
+
+```bash
+git clone https://github.com/rdsciv/ClearPath.git
+cd ClearPath
+npm ci
+cp .env.example .env.local
+```
+
+Set your key in `.env.local`:
+
+```dotenv
+ANTHROPIC_API_KEY=your_anthropic_api_key
+ANTHROPIC_MODEL=claude-sonnet-5
+```
+
+Then start the development server:
+
+```bash
+npm run dev
+```
+
+Open [http://localhost:3000](http://localhost:3000).
+
+## Environment variables
+
+| Variable | Required | Default | Purpose |
+| --- | --- | --- | --- |
+| `ANTHROPIC_API_KEY` | Yes | — | Authenticates server-side requests to the Anthropic API |
+| `ANTHROPIC_MODEL` | No | `claude-sonnet-5` | Selects a compatible model for all three tools |
+
+Environment files are ignored by Git, except for the safe `.env.example` template. Never commit a real API key or expose it through a `NEXT_PUBLIC_` variable.
+
+## Commands
+
+| Command | Purpose |
+| --- | --- |
+| `npm run dev` | Start the local development server |
+| `npm run lint` | Run ESLint |
+| `npm run typecheck` | Run the TypeScript compiler without emitting files |
+| `npm test` | Run unit tests once |
+| `npm run build` | Create a production build |
+| `npm run start` | Serve a production build |
+| `npm run check` | Run lint, type-check, tests, and build in sequence |
+
+## Project structure
+
+```text
+ClearPath/
+├── .github/workflows/ci.yml # Pull-request and master-branch checks
+├── src/
+│ ├── app/
+│ │ ├── api/ # Thin route adapters for each AI tool
+│ │ ├── asam-mapper/ # ASAM documentation-coverage UI
+│ │ ├── doc-reviewer/ # Documentation feedback UI
+│ │ └── treatment-plan/ # Treatment-plan drafting UI
+│ ├── components/ # Navigation, inputs, notices, and results
+│ ├── hooks/
+│ │ └── use-streaming-tool.ts # Shared browser streaming and cancellation
+│ └── lib/
+│ ├── anthropic.ts # Lazy server-only Anthropic client
+│ ├── asam.ts # Fourth Edition labels and status parser
+│ ├── prompts.ts # Tool-specific system prompts
+│ ├── streaming-tool-route.ts # Shared API validation and streaming
+│ └── tool-input.ts # Shared input limits and validation
+└── tests/ # Pure-function unit tests
+```
+
+## API behavior
+
+All three endpoints accept the same JSON shape:
+
+```json
+{
+ "input": "De-identified clinical narrative of at least 50 characters"
+}
+```
+
+Endpoints:
+
+- `POST /api/treatment-plan`
+- `POST /api/asam-mapper`
+- `POST /api/doc-reviewer`
+
+Input is trimmed and must contain 50–5,000 characters. Successful responses use `text/plain; charset=utf-8` and stream incrementally. Validation errors return `400`; a missing API key returns `503`; failures that occur before streaming begins return `502`. API responses include `Cache-Control: no-store`.
+
+## Privacy, security, and responsible use
+
+ClearPath intentionally stores no submissions in an application database. A submission is still processed by the deployed Next.js server and sent to Anthropic, so “no persistence” is not the same as “no disclosure.” Data handling also depends on hosting logs, observability tools, network infrastructure, the Anthropic account's settings, and any agreements in place.
+
+Before any real clinical use:
+
+- Establish an approved de-identification workflow and train users not to paste protected health information (PHI).
+- Complete organizational privacy, security, legal, clinical-safety, and vendor reviews.
+- Confirm whether HIPAA or other laws apply and put required agreements—including a BAA where applicable—in place with every relevant vendor.
+- Review Anthropic's current [privacy and data-handling documentation](https://privacy.anthropic.com/) for the account and API configuration being used.
+- Add authentication, authorization, rate limiting, abuse controls, audit strategy, monitoring, and a retention/logging policy.
+- Keep secrets server-side and rotate them if exposure is suspected.
+- Validate prompts and outputs with qualified clinicians before deployment, and revalidate them whenever the model or clinical standard changes.
+
+The included headers, input bounds, de-identification warning, and non-persistence architecture are useful safeguards, but they do not make a deployment compliant or clinically validated.
+
+## Known limitations
+
+- AI output can be incomplete, inaccurate, or overly confident and must be reviewed by a qualified person.
+- The app does not assign ASAM risk ratings or recommend a level of care.
+- There is no authentication, tenant isolation, rate limiting, usage metering, audit log, database, or EHR integration.
+- Output is rendered as plain text, so Markdown markers from the model remain visible.
+- The ASAM status badges depend on the model following the requested section and status format.
+- There are no browser-level or live Anthropic integration tests; unit tests cover input validation and status parsing.
+- Payer and jurisdictional documentation requirements vary and are not encoded as authoritative rules.
+
+## Deployment
+
+The project can run on any Node.js platform that supports Next.js server routes and streaming responses. Configure `ANTHROPIC_API_KEY` as a server-side secret and, optionally, `ANTHROPIC_MODEL` in the deployment environment.
+
+Do not treat a successful technical deployment as approval for clinical data. Complete the privacy and production-readiness work above before enabling real users.
+
+## Contributing
+
+1. Create a branch from `master`.
+2. Keep clinical claims grounded in current primary sources and flag any change to prompt behavior.
+3. Add or update tests for parser and validation changes.
+4. Run `npm run check`.
+5. Open a pull request describing user impact, safety implications, and validation performed.
+
+## Roadmap ideas
+
+- Structured model output with schema validation instead of Markdown parsing
+- Authentication, rate limits, and per-user usage controls
+- Configurable prompt/model versions with evaluation fixtures
+- Clinician-reviewed quality and safety evaluation suite
+- Accessible rendered Markdown with export options
+- Optional, explicitly governed EHR workflows
+
+## License
+
+No open-source license is currently included. Unless a license is added, default copyright restrictions apply.
diff --git a/next.config.ts b/next.config.ts
index e9ffa30..5840847 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,7 +1,26 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
- /* config options here */
+ poweredByHeader: false,
+ async headers() {
+ return [
+ {
+ source: "/(.*)",
+ headers: [
+ { key: "X-Content-Type-Options", value: "nosniff" },
+ { key: "X-Frame-Options", value: "DENY" },
+ {
+ key: "Referrer-Policy",
+ value: "strict-origin-when-cross-origin",
+ },
+ {
+ key: "Permissions-Policy",
+ value: "camera=(), microphone=(), geolocation=()",
+ },
+ ],
+ },
+ ];
+ },
};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index a6ec683..532bb00 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,8 +8,8 @@
"name": "clearpath-sud",
"version": "0.1.0",
"dependencies": {
- "@anthropic-ai/sdk": "^0.78.0",
- "next": "16.1.6",
+ "@anthropic-ai/sdk": "^0.111.0",
+ "next": "16.2.10",
"react": "19.2.3",
"react-dom": "19.2.3"
},
@@ -19,9 +19,13 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
- "eslint-config-next": "16.1.6",
+ "eslint-config-next": "16.2.10",
"tailwindcss": "^4",
- "typescript": "^5"
+ "typescript": "^5",
+ "vitest": "^4.1.10"
+ },
+ "engines": {
+ "node": ">=20.9.0"
}
},
"node_modules/@alloc/quick-lru": {
@@ -38,12 +42,13 @@
}
},
"node_modules/@anthropic-ai/sdk": {
- "version": "0.78.0",
- "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.78.0.tgz",
- "integrity": "sha512-PzQhR715td/m1UaaN5hHXjYB8Gl2lF9UVhrrGrZeysiF6Rb74Wc9GCB8hzLdzmQtBd1qe89F9OptgB9Za1Ib5w==",
+ "version": "0.111.0",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.111.0.tgz",
+ "integrity": "sha512-1hUqKi+uJQoS5X90+InwHbFAXMvgq0DnsC5hVLEeSRaODiU5WvmqDAcVCmGS2wC0pN9Z8jtWCbWw7JLzeDdm/Q==",
"license": "MIT",
"dependencies": {
- "json-schema-to-ts": "^3.1.1"
+ "json-schema-to-ts": "^3.1.1",
+ "standardwebhooks": "^1.0.0"
},
"bin": {
"anthropic-ai-sdk": "bin/cli"
@@ -58,13 +63,13 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
- "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -73,9 +78,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
- "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -83,21 +88,21 @@
}
},
"node_modules/@babel/core": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
- "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.29.0",
- "@babel/generator": "^7.29.0",
- "@babel/helper-compilation-targets": "^7.28.6",
- "@babel/helper-module-transforms": "^7.28.6",
- "@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",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
@@ -114,14 +119,14 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.29.1",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
- "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.29.0",
- "@babel/types": "^7.29.0",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -131,14 +136,14 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
- "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.28.6",
- "@babel/helper-validator-option": "^7.27.1",
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
@@ -148,9 +153,9 @@
}
},
"node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -158,29 +163,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
- "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.28.6",
- "@babel/types": "^7.28.6"
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
- "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.28.6",
- "@babel/helper-validator-identifier": "^7.28.5",
- "@babel/traverse": "^7.28.6"
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -190,9 +195,9 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -200,9 +205,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
- "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -210,9 +215,9 @@
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -220,27 +225,27 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
- "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.28.6",
- "@babel/types": "^7.28.6"
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
- "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.29.0"
+ "@babel/types": "^7.29.7"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -259,33 +264,33 @@
}
},
"node_modules/@babel/template": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
- "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.28.6",
- "@babel/parser": "^7.28.6",
- "@babel/types": "^7.28.6"
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
- "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.29.0",
- "@babel/generator": "^7.29.0",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.29.0",
- "@babel/template": "^7.28.6",
- "@babel/types": "^7.29.0",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
"debug": "^4.3.1"
},
"engines": {
@@ -293,35 +298,35 @@
}
},
"node_modules/@babel/types": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
- "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.28.5"
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@emnapi/core": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
- "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/wasi-threads": "1.1.0",
+ "@emnapi/wasi-threads": "1.2.2",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
- "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -329,9 +334,9 @@
}
},
"node_modules/@emnapi/wasi-threads": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
- "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1065,15 +1070,15 @@
}
},
"node_modules/@next/env": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz",
- "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz",
+ "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.6.tgz",
- "integrity": "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.10.tgz",
+ "integrity": "sha512-Gs8D2m21VnJeFo9qvYIIqJH94frWerWYu41BprU1pLtRVF7PCQNLiFZZ3fG+iPuj3K83Cwv/rt+msLOy8Qgu3Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1081,9 +1086,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz",
- "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz",
+ "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==",
"cpu": [
"arm64"
],
@@ -1097,9 +1102,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz",
- "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz",
+ "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==",
"cpu": [
"x64"
],
@@ -1113,12 +1118,15 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz",
- "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz",
+ "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==",
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1129,12 +1137,15 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz",
- "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz",
+ "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==",
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1145,12 +1156,15 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz",
- "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz",
+ "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==",
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1161,12 +1175,15 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz",
- "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz",
+ "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==",
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1177,9 +1194,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz",
- "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz",
+ "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==",
"cpu": [
"arm64"
],
@@ -1193,9 +1210,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz",
- "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz",
+ "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==",
"cpu": [
"x64"
],
@@ -1256,6 +1273,317 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@oxc-project/types": {
+ "version": "0.139.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz",
+ "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
+ "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
+ "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
+ "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
+ "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
+ "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
+ "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
+ "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
+ "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
+ "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
+ "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz",
+ "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
+ "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
+ "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
+ "@napi-rs/wasm-runtime": "^1.1.6"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
+ "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
+ "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1263,6 +1591,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@stablelib/base64": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
+ "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -1544,9 +1885,9 @@
}
},
"node_modules/@tybys/wasm-util": {
- "version": "0.10.1",
- "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
- "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1554,6 +1895,24 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -1804,9 +2163,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz",
- "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==",
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1817,13 +2176,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
- "version": "10.2.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz",
- "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^5.0.2"
+ "brace-expansion": "^5.0.5"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -2169,13 +2528,126 @@
"win32"
]
},
- "node_modules/acorn": {
- "version": "8.16.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
- "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "node_modules/@vitest/expect": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
+ "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
"dev": true,
"license": "MIT",
- "bin": {
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.1.10",
+ "@vitest/utils": "4.1.10",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
+ "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.1.10",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
+ "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
+ "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.1.10",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
+ "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.10",
+ "@vitest/utils": "4.1.10",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
+ "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
+ "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.10",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
"acorn": "bin/acorn"
},
"engines": {
@@ -2402,6 +2874,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/ast-types-flow": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -2463,9 +2945,9 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
- "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
+ "version": "2.10.43",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz",
+ "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==",
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.cjs"
@@ -2475,9 +2957,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
+ "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2499,9 +2981,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
- "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "version": "4.28.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz",
+ "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==",
"dev": true,
"funding": [
{
@@ -2519,11 +3001,11 @@
],
"license": "MIT",
"dependencies": {
- "baseline-browser-mapping": "^2.9.0",
- "caniuse-lite": "^1.0.30001759",
- "electron-to-chromium": "^1.5.263",
- "node-releases": "^2.0.27",
- "update-browserslist-db": "^1.2.0"
+ "baseline-browser-mapping": "^2.10.42",
+ "caniuse-lite": "^1.0.30001803",
+ "electron-to-chromium": "^1.5.389",
+ "node-releases": "^2.0.51",
+ "update-browserslist-db": "^1.2.3"
},
"bin": {
"browserslist": "cli.js"
@@ -2593,9 +3075,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001774",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
- "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==",
+ "version": "1.0.30001805",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz",
+ "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==",
"funding": [
{
"type": "opencollective",
@@ -2612,6 +3094,16 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2852,9 +3344,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.302",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
- "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
+ "version": "1.5.389",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz",
+ "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==",
"dev": true,
"license": "ISC"
},
@@ -2996,6 +3488,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-module-lexer": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz",
+ "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -3140,13 +3639,13 @@
}
},
"node_modules/eslint-config-next": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.6.tgz",
- "integrity": "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.10.tgz",
+ "integrity": "sha512-HSybLOY0QKf39i4FWUqPN0xWiNDi6A6UqJmZtgDkS3zMqjXTqULvj/sueXx3cdCG0mVG+qH6k5/qdegklH1d1w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@next/eslint-plugin-next": "16.1.6",
+ "@next/eslint-plugin-next": "16.2.10",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.32.0",
@@ -3499,6 +3998,16 @@
"node": ">=4.0"
}
},
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -3509,6 +4018,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/expect-type": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
+ "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3560,6 +4079,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-sha256": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
+ "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
+ "license": "Unlicense"
+ },
"node_modules/fastq": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
@@ -3628,9 +4153,9 @@
}
},
"node_modules/flatted": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
"dev": true,
"license": "ISC"
},
@@ -3650,6 +4175,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -4472,10 +5012,20 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
- "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -4956,9 +5506,9 @@
}
},
"node_modules/minimatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
- "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4986,9 +5536,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
"funding": [
{
"type": "github",
@@ -5027,14 +5577,14 @@
"license": "MIT"
},
"node_modules/next": {
- "version": "16.1.6",
- "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
- "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==",
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz",
+ "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==",
"license": "MIT",
"dependencies": {
- "@next/env": "16.1.6",
+ "@next/env": "16.2.10",
"@swc/helpers": "0.5.15",
- "baseline-browser-mapping": "^2.8.3",
+ "baseline-browser-mapping": "^2.9.19",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
"styled-jsx": "5.1.6"
@@ -5046,15 +5596,15 @@
"node": ">=20.9.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "16.1.6",
- "@next/swc-darwin-x64": "16.1.6",
- "@next/swc-linux-arm64-gnu": "16.1.6",
- "@next/swc-linux-arm64-musl": "16.1.6",
- "@next/swc-linux-x64-gnu": "16.1.6",
- "@next/swc-linux-x64-musl": "16.1.6",
- "@next/swc-win32-arm64-msvc": "16.1.6",
- "@next/swc-win32-x64-msvc": "16.1.6",
- "sharp": "^0.34.4"
+ "@next/swc-darwin-arm64": "16.2.10",
+ "@next/swc-darwin-x64": "16.2.10",
+ "@next/swc-linux-arm64-gnu": "16.2.10",
+ "@next/swc-linux-arm64-musl": "16.2.10",
+ "@next/swc-linux-x64-gnu": "16.2.10",
+ "@next/swc-linux-x64-musl": "16.2.10",
+ "@next/swc-win32-arm64-msvc": "16.2.10",
+ "@next/swc-win32-x64-msvc": "16.2.10",
+ "sharp": "^0.34.5"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@@ -5080,9 +5630,9 @@
}
},
"node_modules/next/node_modules/postcss": {
- "version": "8.4.31",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
- "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
+ "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
"funding": [
{
"type": "opencollective",
@@ -5099,9 +5649,9 @@
],
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -5127,11 +5677,14 @@
}
},
"node_modules/node-releases": {
- "version": "2.0.27",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
- "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "version": "2.0.51",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
+ "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/object-assign": {
"version": "4.1.1",
@@ -5256,6 +5809,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/obug": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz",
+ "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -5364,6 +5931,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -5371,9 +5945,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5394,9 +5968,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "version": "8.5.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
+ "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
"dev": true,
"funding": [
{
@@ -5414,7 +5988,7 @@
],
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.11",
+ "nanoid": "^3.3.12",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -5599,6 +6173,40 @@
"node": ">=0.10.0"
}
},
+ "node_modules/rolldown": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz",
+ "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.139.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.1.5",
+ "@rolldown/binding-darwin-arm64": "1.1.5",
+ "@rolldown/binding-darwin-x64": "1.1.5",
+ "@rolldown/binding-freebsd-x64": "1.1.5",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.1.5",
+ "@rolldown/binding-linux-arm64-gnu": "1.1.5",
+ "@rolldown/binding-linux-arm64-musl": "1.1.5",
+ "@rolldown/binding-linux-ppc64-gnu": "1.1.5",
+ "@rolldown/binding-linux-s390x-gnu": "1.1.5",
+ "@rolldown/binding-linux-x64-gnu": "1.1.5",
+ "@rolldown/binding-linux-x64-musl": "1.1.5",
+ "@rolldown/binding-openharmony-arm64": "1.1.5",
+ "@rolldown/binding-wasm32-wasi": "1.1.5",
+ "@rolldown/binding-win32-arm64-msvc": "1.1.5",
+ "@rolldown/binding-win32-x64-msvc": "1.1.5"
+ }
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -5900,6 +6508,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5916,6 +6531,30 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/standardwebhooks": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz",
+ "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/base64": "^1.0.0",
+ "fast-sha256": "^1.3.0"
+ }
+ },
+ "node_modules/std-env": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz",
+ "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -6136,15 +6775,32 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
- "picomatch": "^4.0.3"
+ "picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
@@ -6172,9 +6828,9 @@
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6184,6 +6840,16 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/tinyrainbow": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
+ "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -6479,6 +7145,473 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/vite": {
+ "version": "8.1.4",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz",
+ "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.5",
+ "postcss": "^8.5.16",
+ "rolldown": "~1.1.4",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.3.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
+ "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.1.10",
+ "@vitest/mocker": "4.1.10",
+ "@vitest/pretty-format": "4.1.10",
+ "@vitest/runner": "4.1.10",
+ "@vitest/snapshot": "4.1.10",
+ "@vitest/spy": "4.1.10",
+ "@vitest/utils": "4.1.10",
+ "es-module-lexer": "^2.0.0",
+ "expect-type": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^4.0.0-rc.1",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.10",
+ "@vitest/browser-preview": "4.1.10",
+ "@vitest/browser-webdriverio": "4.1.10",
+ "@vitest/coverage-istanbul": "4.1.10",
+ "@vitest/coverage-v8": "4.1.10",
+ "@vitest/ui": "4.1.10",
+ "happy-dom": "*",
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ },
+ "vite": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6584,6 +7717,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
diff --git a/package.json b/package.json
index 732fca0..026ab68 100644
--- a/package.json
+++ b/package.json
@@ -6,11 +6,23 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "eslint"
+ "lint": "eslint",
+ "typecheck": "tsc --noEmit",
+ "test": "vitest run",
+ "check": "npm run lint && npm run typecheck && npm run test && npm run build"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "overrides": {
+ "brace-expansion@<1.1.13": "1.1.13",
+ "next": {
+ "postcss": "8.5.10"
+ }
},
"dependencies": {
- "@anthropic-ai/sdk": "^0.78.0",
- "next": "16.1.6",
+ "@anthropic-ai/sdk": "^0.111.0",
+ "next": "16.2.10",
"react": "19.2.3",
"react-dom": "19.2.3"
},
@@ -20,8 +32,9 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
- "eslint-config-next": "16.1.6",
+ "eslint-config-next": "16.2.10",
"tailwindcss": "^4",
- "typescript": "^5"
+ "typescript": "^5",
+ "vitest": "^4.1.10"
}
}
diff --git a/src/app/api/asam-mapper/route.ts b/src/app/api/asam-mapper/route.ts
index b953129..d552731 100644
--- a/src/app/api/asam-mapper/route.ts
+++ b/src/app/api/asam-mapper/route.ts
@@ -1,39 +1,12 @@
-import { NextRequest } from "next/server";
-import client from "@/lib/anthropic";
import { ASAM_MAPPER_PROMPT } from "@/lib/prompts";
+import { createStreamingToolResponse } from "@/lib/streaming-tool-route";
-export async function POST(req: NextRequest) {
- const { input } = await req.json();
+export const runtime = "nodejs";
- if (!input || typeof input !== "string" || input.trim().length < 50) {
- return new Response("Input must be at least 50 characters.", {
- status: 400,
- });
- }
-
- const stream = await client.messages.stream({
- model: "claude-sonnet-4-20250514",
- max_tokens: 3000,
- system: ASAM_MAPPER_PROMPT,
- messages: [{ role: "user", content: input.trim() }],
- });
-
- const encoder = new TextEncoder();
- const readable = new ReadableStream({
- async start(controller) {
- for await (const event of stream) {
- if (
- event.type === "content_block_delta" &&
- event.delta.type === "text_delta"
- ) {
- controller.enqueue(encoder.encode(event.delta.text));
- }
- }
- controller.close();
- },
- });
-
- return new Response(readable, {
- headers: { "Content-Type": "text/plain; charset=utf-8" },
+export function POST(request: Request) {
+ return createStreamingToolResponse(request, {
+ maxTokens: 3_000,
+ systemPrompt: ASAM_MAPPER_PROMPT,
+ toolName: "ASAM Mapper",
});
}
diff --git a/src/app/api/doc-reviewer/route.ts b/src/app/api/doc-reviewer/route.ts
index db065d1..74fbb0e 100644
--- a/src/app/api/doc-reviewer/route.ts
+++ b/src/app/api/doc-reviewer/route.ts
@@ -1,39 +1,12 @@
-import { NextRequest } from "next/server";
-import client from "@/lib/anthropic";
import { DOC_REVIEWER_PROMPT } from "@/lib/prompts";
+import { createStreamingToolResponse } from "@/lib/streaming-tool-route";
-export async function POST(req: NextRequest) {
- const { input } = await req.json();
+export const runtime = "nodejs";
- if (!input || typeof input !== "string" || input.trim().length < 50) {
- return new Response("Input must be at least 50 characters.", {
- status: 400,
- });
- }
-
- const stream = await client.messages.stream({
- model: "claude-sonnet-4-20250514",
- max_tokens: 2500,
- system: DOC_REVIEWER_PROMPT,
- messages: [{ role: "user", content: input.trim() }],
- });
-
- const encoder = new TextEncoder();
- const readable = new ReadableStream({
- async start(controller) {
- for await (const event of stream) {
- if (
- event.type === "content_block_delta" &&
- event.delta.type === "text_delta"
- ) {
- controller.enqueue(encoder.encode(event.delta.text));
- }
- }
- controller.close();
- },
- });
-
- return new Response(readable, {
- headers: { "Content-Type": "text/plain; charset=utf-8" },
+export function POST(request: Request) {
+ return createStreamingToolResponse(request, {
+ maxTokens: 2_500,
+ systemPrompt: DOC_REVIEWER_PROMPT,
+ toolName: "Documentation Reviewer",
});
}
diff --git a/src/app/api/treatment-plan/route.ts b/src/app/api/treatment-plan/route.ts
index 3f04573..3483352 100644
--- a/src/app/api/treatment-plan/route.ts
+++ b/src/app/api/treatment-plan/route.ts
@@ -1,39 +1,12 @@
-import { NextRequest } from "next/server";
-import client from "@/lib/anthropic";
import { TREATMENT_PLAN_PROMPT } from "@/lib/prompts";
+import { createStreamingToolResponse } from "@/lib/streaming-tool-route";
-export async function POST(req: NextRequest) {
- const { input } = await req.json();
+export const runtime = "nodejs";
- if (!input || typeof input !== "string" || input.trim().length < 50) {
- return new Response("Input must be at least 50 characters.", {
- status: 400,
- });
- }
-
- const stream = await client.messages.stream({
- model: "claude-sonnet-4-20250514",
- max_tokens: 2000,
- system: TREATMENT_PLAN_PROMPT,
- messages: [{ role: "user", content: input.trim() }],
- });
-
- const encoder = new TextEncoder();
- const readable = new ReadableStream({
- async start(controller) {
- for await (const event of stream) {
- if (
- event.type === "content_block_delta" &&
- event.delta.type === "text_delta"
- ) {
- controller.enqueue(encoder.encode(event.delta.text));
- }
- }
- controller.close();
- },
- });
-
- return new Response(readable, {
- headers: { "Content-Type": "text/plain; charset=utf-8" },
+export function POST(request: Request) {
+ return createStreamingToolResponse(request, {
+ maxTokens: 2_000,
+ systemPrompt: TREATMENT_PLAN_PROMPT,
+ toolName: "Treatment Plan Helper",
});
}
diff --git a/src/app/asam-mapper/page.tsx b/src/app/asam-mapper/page.tsx
index 4067b30..0c9849c 100644
--- a/src/app/asam-mapper/page.tsx
+++ b/src/app/asam-mapper/page.tsx
@@ -5,93 +5,19 @@ import TextInput from "@/components/text-input";
import SubmitButton from "@/components/submit-button";
import ResultDisplay from "@/components/result-display";
import DimensionBadge from "@/components/dimension-badge";
-
-type Status = "covered" | "partial" | "missing";
-
-interface DimensionStatus {
- dimension: number;
- label: string;
- status: Status;
-}
-
-const dimensionLabels = [
- "Withdrawal",
- "Biomedical",
- "Emotional/Behavioral",
- "Readiness to Change",
- "Relapse Potential",
- "Recovery Environment",
-];
-
-function parseDimensionStatuses(text: string): DimensionStatus[] {
- const statuses: DimensionStatus[] = [];
- for (let i = 1; i <= 6; i++) {
- const pattern = new RegExp(
- `Dimension ${i}[^]*?\\*\\*Status:\\*\\*\\s*(Covered|Partially Addressed|Missing)`,
- "i"
- );
- const match = text.match(pattern);
- if (match) {
- const raw = match[1].toLowerCase();
- let status: Status = "missing";
- if (raw.includes("covered") && !raw.includes("partial")) {
- status = "covered";
- } else if (raw.includes("partial")) {
- status = "partial";
- }
- statuses.push({
- dimension: i,
- label: dimensionLabels[i - 1],
- status,
- });
- }
- }
- return statuses;
-}
+import ClinicalNotice from "@/components/clinical-notice";
+import { parseDimensionStatuses } from "@/lib/asam";
+import { MIN_INPUT_LENGTH } from "@/lib/tool-input";
+import { useStreamingTool } from "@/hooks/use-streaming-tool";
export default function AsamMapperPage() {
const [input, setInput] = useState("");
- const [result, setResult] = useState("");
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState("");
+ const { error, isLoading, result, stop, submit } =
+ useStreamingTool("/api/asam-mapper");
const dimensionStatuses = parseDimensionStatuses(result);
- async function handleSubmit() {
- if (input.trim().length < 50) return;
-
- setIsLoading(true);
- setResult("");
- setError("");
-
- try {
- const response = await fetch("/api/asam-mapper", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ input }),
- });
-
- if (!response.ok) {
- const text = await response.text();
- throw new Error(text || "Request failed");
- }
-
- const reader = response.body?.getReader();
- const decoder = new TextDecoder();
-
- if (!reader) throw new Error("No response stream");
-
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- setResult((prev) => prev + decoder.decode(value));
- }
- } catch (e) {
- setError(e instanceof Error ? e.message : "Something went wrong");
- } finally {
- setIsLoading(false);
- }
- }
+ const handleSubmit = () => submit(input);
return (
@@ -103,9 +29,11 @@ export default function AsamMapperPage() {
+
+
@@ -114,8 +42,9 @@ export default function AsamMapperPage() {
label="Analyze Dimensions"
loadingLabel="Analyzing..."
isLoading={isLoading}
- disabled={input.trim().length < 50}
+ disabled={input.trim().length < MIN_INPUT_LENGTH}
onClick={handleSubmit}
+ onCancel={stop}
/>
{error && (
@@ -132,7 +61,7 @@ export default function AsamMapperPage() {
)}
-
+
);
}
diff --git a/src/app/doc-reviewer/page.tsx b/src/app/doc-reviewer/page.tsx
index a7f1847..69ee166 100644
--- a/src/app/doc-reviewer/page.tsx
+++ b/src/app/doc-reviewer/page.tsx
@@ -4,48 +4,16 @@ import { useState } from "react";
import TextInput from "@/components/text-input";
import SubmitButton from "@/components/submit-button";
import ResultDisplay from "@/components/result-display";
+import ClinicalNotice from "@/components/clinical-notice";
+import { MIN_INPUT_LENGTH } from "@/lib/tool-input";
+import { useStreamingTool } from "@/hooks/use-streaming-tool";
export default function DocReviewerPage() {
const [input, setInput] = useState("");
- const [result, setResult] = useState("");
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState("");
+ const { error, isLoading, result, stop, submit } =
+ useStreamingTool("/api/doc-reviewer");
- async function handleSubmit() {
- if (input.trim().length < 50) return;
-
- setIsLoading(true);
- setResult("");
- setError("");
-
- try {
- const response = await fetch("/api/doc-reviewer", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ input }),
- });
-
- if (!response.ok) {
- const text = await response.text();
- throw new Error(text || "Request failed");
- }
-
- const reader = response.body?.getReader();
- const decoder = new TextDecoder();
-
- if (!reader) throw new Error("No response stream");
-
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- setResult((prev) => prev + decoder.decode(value));
- }
- } catch (e) {
- setError(e instanceof Error ? e.message : "Something went wrong");
- } finally {
- setIsLoading(false);
- }
- }
+ const handleSubmit = () => submit(input);
return (
@@ -59,6 +27,8 @@ export default function DocReviewerPage() {
+
+
{error && (
@@ -80,7 +51,7 @@ export default function DocReviewerPage() {
)}
-
+
);
}
diff --git a/src/app/globals.css b/src/app/globals.css
index bf3b75a..f74036e 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -3,7 +3,8 @@
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
- --font-sans: var(--font-inter);
+ --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
+ sans-serif;
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index a22a91a..258be3f 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,13 +1,7 @@
import type { Metadata } from "next";
-import { Inter } from "next/font/google";
import Nav from "@/components/nav";
import "./globals.css";
-const inter = Inter({
- variable: "--font-inter",
- subsets: ["latin"],
-});
-
export const metadata: Metadata = {
title: "ClearPath SUD",
description:
@@ -21,7 +15,7 @@ export default function RootLayout({
}>) {
return (
-
+
{children}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index dc95f83..a88fb15 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -42,6 +42,11 @@ export default function Home() {
))}
+
+ ClearPath provides educational drafting support. It does not diagnose,
+ determine level of care, replace supervision, or guarantee payer or
+ regulatory compliance. Do not submit identifiable patient information.
+
);
}
diff --git a/src/app/treatment-plan/page.tsx b/src/app/treatment-plan/page.tsx
index f87b884..f9c00d8 100644
--- a/src/app/treatment-plan/page.tsx
+++ b/src/app/treatment-plan/page.tsx
@@ -4,48 +4,16 @@ import { useState } from "react";
import TextInput from "@/components/text-input";
import SubmitButton from "@/components/submit-button";
import ResultDisplay from "@/components/result-display";
+import ClinicalNotice from "@/components/clinical-notice";
+import { MIN_INPUT_LENGTH } from "@/lib/tool-input";
+import { useStreamingTool } from "@/hooks/use-streaming-tool";
export default function TreatmentPlanPage() {
const [input, setInput] = useState("");
- const [result, setResult] = useState("");
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState("");
+ const { error, isLoading, result, stop, submit } =
+ useStreamingTool("/api/treatment-plan");
- async function handleSubmit() {
- if (input.trim().length < 50) return;
-
- setIsLoading(true);
- setResult("");
- setError("");
-
- try {
- const response = await fetch("/api/treatment-plan", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ input }),
- });
-
- if (!response.ok) {
- const text = await response.text();
- throw new Error(text || "Request failed");
- }
-
- const reader = response.body?.getReader();
- const decoder = new TextDecoder();
-
- if (!reader) throw new Error("No response stream");
-
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- setResult((prev) => prev + decoder.decode(value));
- }
- } catch (e) {
- setError(e instanceof Error ? e.message : "Something went wrong");
- } finally {
- setIsLoading(false);
- }
- }
+ const handleSubmit = () => submit(input);
return (
@@ -59,6 +27,8 @@ export default function TreatmentPlanPage() {
+
+
{error && (
@@ -80,7 +51,7 @@ export default function TreatmentPlanPage() {
)}
-
+
);
}
diff --git a/src/components/clinical-notice.tsx b/src/components/clinical-notice.tsx
new file mode 100644
index 0000000..f50a4e5
--- /dev/null
+++ b/src/components/clinical-notice.tsx
@@ -0,0 +1,16 @@
+export default function ClinicalNotice() {
+ return (
+
+ Protect privacy before pasting.
+
+ Remove names, birth dates, record numbers, and other identifiers. ClearPath
+ is drafting support—not diagnostic, placement, legal, or medical advice.
+ Verify every result against current criteria, organizational policy, and
+ your clinical judgment.
+
+
+ );
+}
diff --git a/src/components/dimension-badge.tsx b/src/components/dimension-badge.tsx
index 50ca41a..e5e31ca 100644
--- a/src/components/dimension-badge.tsx
+++ b/src/components/dimension-badge.tsx
@@ -38,8 +38,12 @@ export default function DimensionBadge({
return (
-
+
D{dimension}: {label}
diff --git a/src/components/nav.tsx b/src/components/nav.tsx
index 2809e90..36e2dc0 100644
--- a/src/components/nav.tsx
+++ b/src/components/nav.tsx
@@ -15,17 +15,18 @@ export default function Nav() {
return (
-
+
ClearPath
-
+
{tabs.map((tab) => {
const isActive = pathname === tab.href;
return (
setCopied(false), 2000);
+ try {
+ await navigator.clipboard.writeText(content);
+ setCopyFailed(false);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch {
+ setCopyFailed(true);
+ }
}
return (
- Result
+ {isStreaming ? "Generating result" : "Result"}
- {copied ? "Copied!" : "Copy to clipboard"}
+ {copyFailed
+ ? "Copy failed"
+ : copied
+ ? "Copied!"
+ : "Copy to clipboard"}
-
diff --git a/src/components/submit-button.tsx b/src/components/submit-button.tsx
index 3a53ea9..d323cb0 100644
--- a/src/components/submit-button.tsx
+++ b/src/components/submit-button.tsx
@@ -6,6 +6,7 @@ interface SubmitButtonProps {
isLoading: boolean;
disabled?: boolean;
onClick: () => void;
+ onCancel?: () => void;
}
export default function SubmitButton({
@@ -14,36 +15,51 @@ export default function SubmitButton({
isLoading,
disabled = false,
onClick,
+ onCancel,
}: SubmitButtonProps) {
return (
-
- {isLoading && (
-
+
+ {isLoading && (
+
+
+
+
+ )}
+ {isLoading ? loadingLabel : label}
+
+ {isLoading && onCancel && (
+
-
-
-
+ Stop
+
)}
- {isLoading ? loadingLabel : label}
-
+
);
}
diff --git a/src/components/text-input.tsx b/src/components/text-input.tsx
index 2bcac1b..6b58621 100644
--- a/src/components/text-input.tsx
+++ b/src/components/text-input.tsx
@@ -1,5 +1,8 @@
"use client";
+import { useId } from "react";
+import { MAX_INPUT_LENGTH, MIN_INPUT_LENGTH } from "@/lib/tool-input";
+
interface TextInputProps {
label: string;
placeholder: string;
@@ -15,19 +18,27 @@ export default function TextInput({
placeholder,
value,
onChange,
- maxLength = 5000,
- minLength = 50,
+ maxLength = MAX_INPUT_LENGTH,
+ minLength = MIN_INPUT_LENGTH,
rows = 10,
}: TextInputProps) {
+ const inputId = useId();
+ const helpId = `${inputId}-help`;
const charCount = value.length;
const isTooShort = charCount > 0 && charCount < minLength;
return (
-
+
{label}
-
+
{isTooShort ? (
Minimum {minLength} characters for useful results
diff --git a/src/hooks/use-streaming-tool.ts b/src/hooks/use-streaming-tool.ts
new file mode 100644
index 0000000..2b5a4c2
--- /dev/null
+++ b/src/hooks/use-streaming-tool.ts
@@ -0,0 +1,75 @@
+"use client";
+
+import { useCallback, useEffect, useRef, useState } from "react";
+
+export function useStreamingTool(endpoint: string) {
+ const [result, setResult] = useState("");
+ const [error, setError] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+ const controllerRef = useRef(null);
+
+ useEffect(() => {
+ return () => controllerRef.current?.abort();
+ }, []);
+
+ const stop = useCallback(() => {
+ controllerRef.current?.abort();
+ }, []);
+
+ const submit = useCallback(
+ async (input: string) => {
+ controllerRef.current?.abort();
+ const controller = new AbortController();
+ controllerRef.current = controller;
+
+ setIsLoading(true);
+ setResult("");
+ setError("");
+
+ try {
+ const response = await fetch(endpoint, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ input }),
+ signal: controller.signal,
+ });
+
+ if (!response.ok) {
+ const message = await response.text();
+ throw new Error(message || "The request could not be completed.");
+ }
+
+ const reader = response.body?.getReader();
+ if (!reader) throw new Error("The response did not include a stream.");
+
+ const decoder = new TextDecoder();
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ setResult((current) => current + decoder.decode(value, { stream: true }));
+ }
+
+ const remainder = decoder.decode();
+ if (remainder) setResult((current) => current + remainder);
+ } catch (requestError) {
+ if (requestError instanceof Error && requestError.name === "AbortError") {
+ return;
+ }
+
+ setError(
+ requestError instanceof Error
+ ? requestError.message
+ : "Something went wrong. Please try again.",
+ );
+ } finally {
+ if (controllerRef.current === controller) {
+ controllerRef.current = null;
+ setIsLoading(false);
+ }
+ }
+ },
+ [endpoint],
+ );
+
+ return { error, isLoading, result, stop, submit };
+}
diff --git a/src/lib/anthropic.ts b/src/lib/anthropic.ts
index 574805a..43fcbe4 100644
--- a/src/lib/anthropic.ts
+++ b/src/lib/anthropic.ts
@@ -1,5 +1,15 @@
+import "server-only";
import Anthropic from "@anthropic-ai/sdk";
-const client = new Anthropic();
+let client: Anthropic | undefined;
-export default client;
+export function getAnthropicClient() {
+ const apiKey = process.env.ANTHROPIC_API_KEY?.trim();
+
+ if (!apiKey) {
+ throw new Error("ANTHROPIC_API_KEY is not configured.");
+ }
+
+ client ??= new Anthropic({ apiKey });
+ return client;
+}
diff --git a/src/lib/asam.ts b/src/lib/asam.ts
new file mode 100644
index 0000000..b80070a
--- /dev/null
+++ b/src/lib/asam.ts
@@ -0,0 +1,48 @@
+export type DimensionStatusValue = "covered" | "partial" | "missing";
+
+export interface DimensionStatus {
+ dimension: number;
+ label: string;
+ status: DimensionStatusValue;
+}
+
+export const ASAM_DIMENSION_LABELS = [
+ "Intoxication, Withdrawal & Medications",
+ "Biomedical Conditions",
+ "Psychiatric & Cognitive Conditions",
+ "Substance Use-Related Risks",
+ "Recovery Environment",
+ "Person-Centered Considerations",
+] as const;
+
+export function parseDimensionStatuses(text: string): DimensionStatus[] {
+ const statuses: DimensionStatus[] = [];
+
+ for (let dimension = 1; dimension <= ASAM_DIMENSION_LABELS.length; dimension++) {
+ const sectionPattern = new RegExp(
+ `(?:^|\\n)#{1,6}\\s*Dimension ${dimension}\\b[\\s\\S]*?(?=\\n#{1,6}\\s*(?:Dimension \\d|Summary)\\b|$)`,
+ "i",
+ );
+ const section = text.match(sectionPattern)?.[0];
+ const match = section?.match(
+ /\*\*Status:\*\*\s*(Covered|Partially Addressed|Missing)/i,
+ );
+
+ if (!match) continue;
+
+ const rawStatus = match[1].toLowerCase();
+ const status: DimensionStatusValue = rawStatus.includes("partial")
+ ? "partial"
+ : rawStatus === "covered"
+ ? "covered"
+ : "missing";
+
+ statuses.push({
+ dimension,
+ label: ASAM_DIMENSION_LABELS[dimension - 1],
+ status,
+ });
+ }
+
+ return statuses;
+}
diff --git a/src/lib/prompts.ts b/src/lib/prompts.ts
index c0aa7b6..4342617 100644
--- a/src/lib/prompts.ts
+++ b/src/lib/prompts.ts
@@ -25,16 +25,16 @@ Guidelines:
- Use an encouraging, educational tone appropriate for trainees
- If the input is not clinical in nature, politely redirect: explain what kind of input this tool expects`;
-export const ASAM_MAPPER_PROMPT = `You are an experienced clinical supervisor specializing in ASAM (American Society of Addiction Medicine) criteria for substance use disorder assessment. You help trainees ensure their assessments are comprehensive.
+export const ASAM_MAPPER_PROMPT = `You are an experienced clinical supervisor specializing in The ASAM Criteria, Fourth Edition, for adult substance use disorder assessment. You help trainees evaluate documentation completeness. You do not calculate a risk rating, recommend a level of care, or make a clinical determination.
Analyze the assessment narrative provided and evaluate coverage of all 6 ASAM dimensions:
-1. **Acute Intoxication and/or Withdrawal Potential** — Current and past substance use patterns, withdrawal history and risk, current intoxication status
-2. **Biomedical Conditions and Complications** — Physical health status, chronic conditions, medications, pregnancy, infectious diseases
-3. **Emotional, Behavioral, or Cognitive Conditions** — Mental health diagnoses, trauma history, cognitive functioning, emotional regulation, suicide/self-harm risk
-4. **Readiness to Change** — Motivation level, stage of change, treatment engagement history, ambivalence, personal goals
-5. **Relapse, Continued Use, or Continued Problem Potential** — Relapse history, triggers, coping skills, patterns of use, ability to maintain gains
-6. **Recovery/Living Environment** — Housing stability, social support, family dynamics, employment, legal issues, access to recovery community
+1. **Intoxication, Withdrawal, and Addiction Medications** — Intoxication and associated risks, withdrawal and associated risks, and addiction medication needs
+2. **Biomedical Conditions** — Physical health concerns, pregnancy-related concerns when applicable, and sleep problems
+3. **Psychiatric and Cognitive Conditions** — Active psychiatric concerns, persistent disability, cognitive functioning, trauma exposure and related needs, and relevant history
+4. **Substance Use-Related Risks** — Likelihood of risky substance use and likelihood of risky substance-use-disorder-related behaviors
+5. **Recovery Environment Interactions** — The person's ability to function in, safety within, and support from the current environment, plus relevant cultural perceptions of substance use
+6. **Person-Centered Considerations** — Patient preferences, barriers to care, and need for motivational enhancement
For each dimension, provide:
@@ -50,9 +50,10 @@ A brief overall assessment of the narrative's completeness, highlighting the str
Guidelines:
- Use person-first language throughout
-- Do not diagnose — you are evaluating documentation completeness, not the client
+- Do not diagnose, assign risk ratings, recommend a level of care, or imply that coverage equals clinical adequacy
- Do not fabricate details not present in the input
- Be specific about what is present and what is missing
+- Treat motivation and readiness as considerations integrated across dimensions and treatment planning, not as a standalone Fourth Edition dimension
- Use an encouraging, educational tone appropriate for trainees
- If the input is not clinical in nature, politely redirect: explain what kind of input this tool expects`;
diff --git a/src/lib/streaming-tool-route.ts b/src/lib/streaming-tool-route.ts
new file mode 100644
index 0000000..006974f
--- /dev/null
+++ b/src/lib/streaming-tool-route.ts
@@ -0,0 +1,97 @@
+import "server-only";
+import { getAnthropicClient } from "@/lib/anthropic";
+import { validateToolInput } from "@/lib/tool-input";
+
+interface StreamingToolOptions {
+ maxTokens: number;
+ systemPrompt: string;
+ toolName: string;
+}
+
+const DEFAULT_MODEL = "claude-sonnet-5";
+
+function textResponse(message: string, status: number) {
+ return new Response(message, {
+ status,
+ headers: {
+ "Cache-Control": "no-store",
+ "Content-Type": "text/plain; charset=utf-8",
+ },
+ });
+}
+
+export async function createStreamingToolResponse(
+ request: Request,
+ options: StreamingToolOptions,
+) {
+ let payload: unknown;
+
+ try {
+ payload = await request.json();
+ } catch {
+ return textResponse("Request body must be valid JSON.", 400);
+ }
+
+ const validation = validateToolInput(payload);
+ if (!validation.ok) {
+ return textResponse(validation.message, 400);
+ }
+
+ if (!process.env.ANTHROPIC_API_KEY?.trim()) {
+ console.error(`${options.toolName}: ANTHROPIC_API_KEY is not configured.`);
+ return textResponse("The AI service is not configured.", 503);
+ }
+
+ try {
+ const stream = getAnthropicClient().messages.stream(
+ {
+ model: process.env.ANTHROPIC_MODEL?.trim() || DEFAULT_MODEL,
+ max_tokens: options.maxTokens,
+ thinking: { type: "disabled" },
+ system: options.systemPrompt,
+ messages: [{ role: "user", content: validation.input }],
+ },
+ { signal: request.signal },
+ );
+
+ const encoder = new TextEncoder();
+ const body = new ReadableStream({
+ async start(controller) {
+ try {
+ for await (const event of stream) {
+ if (
+ event.type === "content_block_delta" &&
+ event.delta.type === "text_delta"
+ ) {
+ controller.enqueue(encoder.encode(event.delta.text));
+ }
+ }
+ controller.close();
+ } catch (error) {
+ if (!request.signal.aborted) {
+ const errorName = error instanceof Error ? error.name : "UnknownError";
+ console.error(
+ `${options.toolName}: response stream failed (${errorName}).`,
+ );
+ controller.error(error);
+ }
+ }
+ },
+ cancel() {
+ stream.abort();
+ },
+ });
+
+ return new Response(body, {
+ headers: {
+ "Cache-Control": "no-store",
+ "Content-Type": "text/plain; charset=utf-8",
+ "X-Content-Type-Options": "nosniff",
+ },
+ });
+ } catch (error) {
+ const errorName = error instanceof Error ? error.name : "UnknownError";
+ console.error(`${options.toolName}: request failed (${errorName}).`);
+ return textResponse("The AI service could not complete the request.", 502);
+ }
+}
diff --git a/src/lib/tool-input.ts b/src/lib/tool-input.ts
new file mode 100644
index 0000000..a902b4d
--- /dev/null
+++ b/src/lib/tool-input.ts
@@ -0,0 +1,34 @@
+export const MIN_INPUT_LENGTH = 50;
+export const MAX_INPUT_LENGTH = 5_000;
+
+type ValidInput = { ok: true; input: string };
+type InvalidInput = { ok: false; message: string };
+
+export function validateToolInput(payload: unknown): ValidInput | InvalidInput {
+ if (
+ typeof payload !== "object" ||
+ payload === null ||
+ !("input" in payload) ||
+ typeof payload.input !== "string"
+ ) {
+ return { ok: false, message: "Input must be a string." };
+ }
+
+ const input = payload.input.trim();
+
+ if (input.length < MIN_INPUT_LENGTH) {
+ return {
+ ok: false,
+ message: `Input must be at least ${MIN_INPUT_LENGTH} characters.`,
+ };
+ }
+
+ if (input.length > MAX_INPUT_LENGTH) {
+ return {
+ ok: false,
+ message: `Input must be no more than ${MAX_INPUT_LENGTH} characters.`,
+ };
+ }
+
+ return { ok: true, input };
+}
diff --git a/tests/asam.test.ts b/tests/asam.test.ts
new file mode 100644
index 0000000..9de92fa
--- /dev/null
+++ b/tests/asam.test.ts
@@ -0,0 +1,64 @@
+import { describe, expect, it } from "vitest";
+import { parseDimensionStatuses } from "../src/lib/asam";
+
+describe("parseDimensionStatuses", () => {
+ it("parses all supported status values from a completed response", () => {
+ const response = `
+## Dimension 1: Intoxication, Withdrawal, and Addiction Medications
+**Status:** Covered
+
+## Dimension 2: Biomedical Conditions
+**Status:** Partially Addressed
+
+## Dimension 3: Psychiatric and Cognitive Conditions
+**Status:** Missing
+`;
+
+ expect(parseDimensionStatuses(response)).toEqual([
+ {
+ dimension: 1,
+ label: "Intoxication, Withdrawal & Medications",
+ status: "covered",
+ },
+ {
+ dimension: 2,
+ label: "Biomedical Conditions",
+ status: "partial",
+ },
+ {
+ dimension: 3,
+ label: "Psychiatric & Cognitive Conditions",
+ status: "missing",
+ },
+ ]);
+ });
+
+ it("returns only dimensions whose status has arrived in a partial stream", () => {
+ const partialResponse = `
+## Dimension 1: Intoxication, Withdrawal, and Addiction Medications
+**Status:** Covered
+
+## Dimension 2: Biomedical Conditions
+`;
+
+ expect(parseDimensionStatuses(partialResponse)).toHaveLength(1);
+ });
+
+ it("does not allow one dimension section to consume a later status", () => {
+ const response = `
+## Dimension 2: Biomedical Conditions
+No status was emitted.
+
+## Dimension 3: Psychiatric and Cognitive Conditions
+**Status:** Covered
+`;
+
+ expect(parseDimensionStatuses(response)).toEqual([
+ {
+ dimension: 3,
+ label: "Psychiatric & Cognitive Conditions",
+ status: "covered",
+ },
+ ]);
+ });
+});
diff --git a/tests/tool-input.test.ts b/tests/tool-input.test.ts
new file mode 100644
index 0000000..1bb3062
--- /dev/null
+++ b/tests/tool-input.test.ts
@@ -0,0 +1,44 @@
+import { describe, expect, it } from "vitest";
+import {
+ MAX_INPUT_LENGTH,
+ MIN_INPUT_LENGTH,
+ validateToolInput,
+} from "../src/lib/tool-input";
+
+describe("validateToolInput", () => {
+ it("trims and accepts input within the supported bounds", () => {
+ const input = "x".repeat(MIN_INPUT_LENGTH);
+ expect(validateToolInput({ input: ` ${input} ` })).toEqual({
+ ok: true,
+ input,
+ });
+ });
+
+ it.each([null, {}, { input: 42 }, "text"])(
+ "rejects a malformed payload: %j",
+ (payload) => {
+ expect(validateToolInput(payload)).toEqual({
+ ok: false,
+ message: "Input must be a string.",
+ });
+ },
+ );
+
+ it("rejects input below the minimum", () => {
+ expect(validateToolInput({ input: "x".repeat(MIN_INPUT_LENGTH - 1) })).toEqual(
+ {
+ ok: false,
+ message: `Input must be at least ${MIN_INPUT_LENGTH} characters.`,
+ },
+ );
+ });
+
+ it("rejects input above the server-side maximum", () => {
+ expect(validateToolInput({ input: "x".repeat(MAX_INPUT_LENGTH + 1) })).toEqual(
+ {
+ ok: false,
+ message: `Input must be no more than ${MAX_INPUT_LENGTH} characters.`,
+ },
+ );
+ });
+});