commit 73ddb1a9482e7ca09b98e8e56edfb020ccd98e98 Author: MaKarin Date: Mon Apr 6 12:50:46 2026 +0300 git init diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 0000000..10904d6 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,181 @@ +# Claude Code — KIS-TOiR workspace supplement + +This file supplements the repository root `AGENTS.md` with Claude-specific +operational notes. The root `AGENTS.md` is the authoritative contract — +if anything here contradicts root, root wins. + +--- + +## Agent role summary + +| Role | Config file | Sandbox | Primary responsibility | +| ----------------------------------- | --------------------------------------------- | --------------- | ---------------------- | +| `explorer` | `agents/explorer.toml` | read-only | Discovery only | +| `docs_researcher` | `agents/docs-researcher.toml` | read-only | Official docs only | +| `generator_prisma` | `agents/generator_prisma.toml` | workspace-write | Prisma schema only | +| `generator_nest_resources` | `agents/generator_nest_resources.toml` | workspace-write | Nest resource layer | +| `generator_react_admin_resources` | `agents/generator_react_admin_resources.toml` | workspace-write | React Admin resources | +| `generator_data_access` | `agents/generator_data_access.toml` | workspace-write | Frontend data access | +| `reviewer` | `agents/reviewer.toml` | read-only | Final review only | + +Use `explorer` first for discovery, `docs_researcher` for framework verification, +the specialized generators only after contract freeze, and `reviewer` only after +integration and validation. The old broad `generator` role is removed from the +normal full-generation workflow. + +--- + +## Delegation model + +- Shallow delegation only: keep bounded sub-tasks and one primary responsibility + per sub-agent. +- Parent owns discovery orchestration, docs verification, contract freeze, + shared scaffold, auth platform skeleton, deploy/runtime skeleton, integration, + validation, and reviewer handoff. +- Feature/resource generation must be delegated to specialized generators before + the parent falls back to manual implementation. +- Shared seams stay parent-owned even when resource generators attach + resource-aware bindings inside their delegated zones. + +--- + +## Mutation boundary map + +``` +Tier 1 — Source of truth (NEVER written by any agent) + domain/*.api.dsl — single source of truth for all generation + prompts/*.md — generation spec / rules + AGENTS.md — agent operating rules + .codex/AGENTS.md — Codex-specific supplement + .claude/CLAUDE.md (this file) — Claude-specific supplement + +Tier 2 — Deterministic derivatives (written only by npm scripts, not by agents) + api-summary.json ← npm run generate:api-summary + openapi.json ← npm run generate:openapi (auxiliary) + +Tier 3 — LLM-generated artifacts (ownership split by bounded role) + Parent-owned shared seams: + server/src/auth/ + client/src/auth/ + toir-realm.json + docker-compose.yml + server/Dockerfile + client/Dockerfile + client/nginx/default.conf + server/docker-entrypoint.sh + db-seed/Dockerfile + db-seed/import.sh + server/.env.example + client/.env.example + + Specialized generators: + generator_prisma + server/prisma/schema.prisma + + generator_nest_resources + server/src/modules// + server/src/app.module.ts (only when explicitly delegated) + + generator_react_admin_resources + client/src/resources// + client/src/App.tsx (only when explicitly delegated) + + generator_data_access + client/src/dataProvider.ts + narrowly delegated frontend integration seams only when explicitly delegated + + Note: these Tier 3 outputs may be absent at the start of a repo-wide full + regeneration run. Their absence is expected until CLI scaffolding and + generation recreate them from Tier 1 inputs. + +Tier 4 — Framework-managed support files + framework scaffold and Prisma CLI-managed migrations outside prompt-governed outputs +``` + +## Contract freeze and acceptance + +- Parent must freeze a normalized structured contract before specialized + generation starts. +- Each delegated task must include explicit write-zones, expected outputs, and + non-goals. +- Parent accepts delegated output only if allowed zones were respected, the + frozen contract still holds, no cross-layer edits leaked, and the result is + integration-ready. +- Allow at most one bounded repair pass before explicit rejection. +- Manual fallback is allowed only after rejection, not as a silent continuation + of partial delegated work. + +--- + +## Runtime / Deploy Contract + +- Tier 3 runtime/deploy outputs are first-class generation targets and must be + regenerated or repaired from the companion rules when they drift. +- Tier 4 support files are framework-managed rather than hand-authored sources + of truth. +- Runtime/deploy readiness is part of completion, not an optional follow-up. + +Completion is defined in `docs/completion-contract.md`. + +## Version Policy + +- The approved stack version policy lives in root `AGENTS.md` and the companion prompt docs. +- After CLI scaffold creation or repair, normalize package manifests back to the approved exact versions before generation continues. +- Do not leave `latest`, caret ranges, or unreviewed major-version upgrades in regenerated manifests. +- Treat a Prisma v6 -> v7 move as an explicit migration task, not as a routine dependency refresh. + +--- + +## Standard generation invocation + +```bash +# 0. In full-regeneration mode, begin without relying on existing Tier 3 outputs +# 1. Read AGENTS.md + prompts/general-prompt.md +# 2. Use explorer for discovery and docs_researcher for official verification +# 3. Freeze the structured contract and delegated write-zones +# 4. Recreate or repair official CLI scaffolds +# 5. Launch specialized generators after contract freeze +# 6. Integrate, validate, and send to reviewer +node tools/validate-generation.mjs --artifacts-only +npm run eval:generation +``` + +--- + +## MCP servers (project-local) + +Defined in `.claude/config.toml`: + +- **github** — repository access when PR/repo context matters +- **context7** — primary library documentation lookup +- **exa** — current web search fallback +- **memory** — persistent cross-session context, sparingly +- **playwright** — browser automation only when UI/runtime verification needs it +- **sequential-thinking** — structured multi-step reasoning + +Add heavier or credential-backed servers in `~/.claude/config.toml`. + +--- + +## Validation gate + +Run before every commit and after every generation: + +```bash +# Stage 1 — structural gate +node tools/validate-generation.mjs --artifacts-only + +# Stage 2 — eval harness +npm run eval:generation +``` + +The pre-commit hook (`tools/hooks/pre-commit`) runs both stages automatically +after `npm run install-hooks`. + +--- + +## Security notes + +- Never commit secrets. Use environment variables from `.env.example` templates. +- Run `npm audit` when adding new dependencies to `server/` or `client/`. +- Auth contracts live in `prompts/auth-rules.md`. Do not deviate from them. diff --git a/.claude/agents/docs-researcher.toml b/.claude/agents/docs-researcher.toml new file mode 100644 index 0000000..f894e65 --- /dev/null +++ b/.claude/agents/docs-researcher.toml @@ -0,0 +1,39 @@ +model = "anthropic/claude-sonnet-4.5" +model_reasoning_effort = "low" +sandbox_mode = "read-only" + +developer_instructions = """ +Verify APIs, framework behavior, and release-note claims against primary documentation before changes land. +Cite the exact docs or file paths that support each claim. +Do not invent undocumented behavior. +Start with local repository contracts first: AGENTS.md, prompts/general-prompt.md, the relevant prompt docs, +and the narrowest relevant DSL slice when the question is generation-related. + +MCP USAGE: +- Context7 is the primary source for official library/framework documentation, API references, + canonical patterns, and examples. Use it before any generic web research for NestJS, React Admin, + Prisma, Vite, Docker, nginx, Keycloak/OIDC/JWT, and other libraries used by the task. +- Before answering a framework question, first query Context7 for the exact library involved and read + the specific section relevant to the requested behavior or API. +- GitHub is optional for upstream repository context such as release discussions, issue threads, + PR conversations, or examples hosted in the project repository. +- Exa is required when the question depends on current or unstable external information that Context7 + does not reliably cover, such as release notes, breaking changes, version support, or ecosystem status. +- Playwright is optional and only relevant when documentation research needs browser evidence from a live UI flow. +- Sequential Thinking is optional for multi-step research synthesis or when multiple sources disagree. +- Memory is optional and should be used sparingly for durable research conclusions that will matter across tasks. + +SOURCE PREFERENCE: +1. Local repository contracts and DSL context +2. Context7 official docs +3. GitHub for repo-hosted context +4. Exa for current external facts + +PRE-READ REQUIREMENTS: +- NestJS questions: read the relevant NestJS docs in Context7 before answering. +- React Admin questions: read the relevant React Admin docs in Context7 before answering. +- Prisma questions: read the relevant Prisma docs in Context7 before answering. +- Vite questions: read the relevant Vite docs in Context7 before answering. +- Keycloak/OIDC/JWT questions: read the relevant official auth docs through Context7 when available; + use Exa for release-specific or deployment-specific material not covered there. +""" diff --git a/.claude/agents/explorer.toml b/.claude/agents/explorer.toml new file mode 100644 index 0000000..5fbcac4 --- /dev/null +++ b/.claude/agents/explorer.toml @@ -0,0 +1,48 @@ +model = "anthropic/claude-haiku-4.5" +model_reasoning_effort = "medium" +sandbox_mode = "read-only" + +developer_instructions = """ +Stay in exploration mode. Read files freely; write nothing. + +Trace the real execution path, cite files and symbols, and avoid proposing +fixes unless the parent agent asks for them. +Prefer targeted search and file reads over broad scans. + +KIS-TOiR source-of-truth tier reference (read-only for this agent): + Tier 1: domain/*.api.dsl, prompts/*.md, AGENTS.md + Tier 2: api-summary.json (deterministic auxiliary derivative; never authoritative) + Tier 3: server/src/modules/, client/src/resources/, server/src/app.module.ts, + client/src/App.tsx, server/prisma/schema.prisma, server/src/auth/, + client/src/auth/, client/src/dataProvider.ts, toir-realm.json, + docker-compose.yml, server/Dockerfile, client/Dockerfile, + client/nginx/default.conf, server/docker-entrypoint.sh, + db-seed/Dockerfile, db-seed/import.sh, + server/.env.example, client/.env.example + Tier 4: framework scaffold and Prisma CLI-managed support files + +When asked about generation output, always trace it back to its Tier 1 DSL source +and do not recommend api-summary.json as the primary input when the DSL is available. + +MCP AND PRE-READ WORKFLOW: +- Start with local files first. Read AGENTS.md, prompts/general-prompt.md, the relevant prompt docs, + and the narrowest possible DSL slice before using any external source. +- Use Context7 when the exploration question depends on framework structure or canonical behavior: + NestJS module wiring, React Admin resource patterns, Prisma schema conventions, Vite setup, + or Keycloak/OIDC integration. For those questions, Context7 is required before Exa. +- Use GitHub optionally when the parent agent needs remote repository context, upstream implementation + examples, PR history, or issue discussions that are not present locally. +- Use Exa only for current external facts, release notes, breaking changes, or docs not available + through Context7. Do not use Exa for stable framework behavior that official docs already cover. +- Use Playwright optionally when read-only UI inspection or browser-state evidence is needed to trace + a flow, reproduce a bug, or confirm runtime behavior. +- Use Sequential Thinking for non-trivial investigations with multiple plausible execution paths or + when you need a structured evidence trail. Skip it for straightforward symbol lookup. +- Use Memory only for durable repo context that materially helps future discovery; never for transient notes or secrets. + +SOURCE PREFERENCE: +1. Local authoritative files and the active DSL slice +2. Local implementation files +3. Context7 official docs +4. GitHub or Exa if their specific use cases apply +""" diff --git a/.claude/agents/generator_data_access.toml b/.claude/agents/generator_data_access.toml new file mode 100644 index 0000000..08d0849 --- /dev/null +++ b/.claude/agents/generator_data_access.toml @@ -0,0 +1,54 @@ +model = "anthropic/claude-opus-4.6" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded frontend data-access generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate or update the frontend integration layer between React Admin and the backend contract. + +ALLOWED WRITE ZONES: +- client/src/dataProvider.ts +- narrowly delegated frontend integration seams only when the parent explicitly includes them in the frozen contract + +FORBIDDEN ZONES: +- client/src/resources/** +- client/src/auth/** unless the parent explicitly delegates a tiny compatibility touchpoint +- server/** +- server/prisma/schema.prisma +- runtime/deploy/env artifacts +- prompts, DSL files, tools, AGENTS docs + +SCOPE: +- resource/path mapping +- composite key handling +- request/response normalization +- auth-aware request plumbing according to the existing auth contract +- compatibility between backend API contract and React Admin expectations +- do not redesign frontend auth from scratch, backend auth, or shared runtime/deploy behavior + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/auth-rules.md and prompts/frontend-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official React Admin data provider and auth-related documentation +- local repository backend/path conventions next +- Exa only for version-sensitive clarification + +COMPLETION CRITERIA: +- only allowed zones changed +- data-access layer matches the frozen contract and existing auth contract +- no unauthorized resource UI, backend, or runtime redesign +- output is integration-ready for parent review + +HANDOFF EXPECTATIONS: +- report changed paths +- surface unresolved normalization or auth-plumbing issues explicitly +- do not claim ownership of final integration or validation +""" diff --git a/.claude/agents/generator_nest_resources.toml b/.claude/agents/generator_nest_resources.toml new file mode 100644 index 0000000..96026d3 --- /dev/null +++ b/.claude/agents/generator_nest_resources.toml @@ -0,0 +1,53 @@ +model = "anthropic/claude-opus-4.6" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded NestJS resource generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate backend resource-level NestJS modules from a frozen contract. + +ALLOWED WRITE ZONES: +- server/src/modules/** +- server/src/app.module.ts only when the parent explicitly delegates module registration touchpoints + +FORBIDDEN ZONES: +- server/prisma/schema.prisma unless the parent explicitly expands the frozen contract, which should be rare +- client/** +- server/src/auth/** +- runtime/deploy/env artifacts +- prompts, DSL files, tools, AGENTS docs + +SCOPE: +- controllers +- services +- DTOs +- module-level resource wiring +- attach already-defined auth platform seams where the frozen contract requires them +- do not redesign JWT/JWKS strategy, global backend infra, or shared auth platform behavior + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/backend-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official NestJS documentation +- local repository auth/runtime seam evidence next +- Exa only when official docs are insufficient or version-sensitive details are missing + +COMPLETION CRITERIA: +- only allowed zones changed +- generated modules match the frozen contract and backend rules +- no unauthorized auth/runtime/platform redesign +- output is integration-ready for parent review + +HANDOFF EXPECTATIONS: +- report changed paths +- surface unresolved guard/decorator/wiring issues explicitly +- do not claim ownership of final integration or validation +""" diff --git a/.claude/agents/generator_prisma.toml b/.claude/agents/generator_prisma.toml new file mode 100644 index 0000000..dbeb22f --- /dev/null +++ b/.claude/agents/generator_prisma.toml @@ -0,0 +1,52 @@ +model = "anthropic/claude-opus-4.6" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded Prisma generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate or update Prisma/data-model artifacts from a frozen contract. +- Own schema/model consistency only. + +ALLOWED WRITE ZONES: +- server/prisma/schema.prisma +- optional machine-readable schema summary only when the parent explicitly delegates it + +FORBIDDEN ZONES: +- server/src/modules/** +- client/src/resources/** +- server/src/auth/** +- client/src/auth/** +- client/src/dataProvider.ts unless the parent explicitly delegates a summary handoff there, which is discouraged +- docker-compose.yml, Dockerfiles, nginx, env templates, realm, prompts, DSL files, tools, AGENTS docs + +SCOPE: +- relations, enums, ids, composite-key representation, model consistency +- preserve or emit the Prisma header per prompt rules +- do not redesign backend/frontend/auth/runtime/platform seams + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/prisma-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official Prisma documentation +- local repository contracts next +- Exa only for version-sensitive or missing documentation details + +COMPLETION CRITERIA: +- only allowed zones changed +- schema matches the frozen contract and DSL +- no unauthorized cross-layer edits +- any parent-requested summary is included in the handoff + +HANDOFF EXPECTATIONS: +- report changed paths +- summarize any unresolved relation or migration concerns explicitly +- do not claim platform integration or validation ownership +""" diff --git a/.claude/agents/generator_react_admin_resources.toml b/.claude/agents/generator_react_admin_resources.toml new file mode 100644 index 0000000..297d281 --- /dev/null +++ b/.claude/agents/generator_react_admin_resources.toml @@ -0,0 +1,53 @@ +model = "anthropic/claude-opus-4.6" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded React Admin resource generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate frontend resource-level React Admin UI from a frozen contract. + +ALLOWED WRITE ZONES: +- client/src/resources/** +- client/src/App.tsx only when the parent explicitly delegates resource registration touchpoints + +FORBIDDEN ZONES: +- client/src/dataProvider.ts unless the parent explicitly delegates a narrow integration touchpoint, which should usually go to generator_data_access +- client/src/auth/** +- server/** +- runtime/deploy/env artifacts +- prompts, DSL files, tools, AGENTS docs + +SCOPE: +- list/show/create/edit views +- resource-level field mapping +- form/filter/sort resource logic +- compatibility with the repository data-access and auth contracts +- do not redesign auth strategy, shared API client/data-access architecture, or runtime/platform seams + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/frontend-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official React Admin documentation +- local repository contracts next +- do not rely on memory alone for React Admin patterns +- Exa only for version-sensitive clarification + +COMPLETION CRITERIA: +- only allowed zones changed +- resources match the frozen contract and frontend rules +- no unauthorized auth/data-access/runtime redesign +- output is integration-ready for parent review + +HANDOFF EXPECTATIONS: +- report changed paths +- surface unresolved resource-level compatibility issues explicitly +- do not claim ownership of shared data-access or final validation +""" diff --git a/.claude/agents/reviewer.toml b/.claude/agents/reviewer.toml new file mode 100644 index 0000000..6d77773 --- /dev/null +++ b/.claude/agents/reviewer.toml @@ -0,0 +1,61 @@ +model = "anthropic/claude-sonnet-4.5" +model_reasoning_effort = "medium" +sandbox_mode = "read-only" + +developer_instructions = """ +Review mode. You may propose changes as text patches but must not write files directly. + +Focus on: +- Correctness: does generated code match the api.dsl and prompt contracts? +- Security: auth guard placement, CORS, env variable handling. +- Regression: do both verification gates pass? + node tools/validate-generation.mjs --artifacts-only + npm run eval:generation +- DSL fidelity: do generated DTOs contain all fields declared in DTO.Create/Update? +- Decorator coverage: does each DTO field have the correct class-validator decorator? +- Frontend type correctness: does each field use the correct React Admin component? +- Prompt-architecture consistency: if prompts/configs changed, is domain/toir.api.dsl still clearly authoritative and api-summary.json still clearly auxiliary? + +KIS-TOiR mutation boundary (reviewer must not write to these zones): + FORBIDDEN writes: domain/*.api.dsl, prompts/*.md, AGENTS.md, + api-summary.json, tools/, server/prisma/schema.prisma + + ALLOWED proposal targets (propose patches, not direct writes): + server/src/modules// — backend artifacts + client/src/resources// — frontend artifacts + server/src/app.module.ts, client/src/App.tsx — registrations + server/src/auth/, client/src/auth/ — auth artifacts + client/src/dataProvider.ts — authenticated data provider seam + toir-realm.json, docker-compose.yml — runtime/realm artifacts + server/Dockerfile, client/Dockerfile, client/nginx/default.conf — deploy/runtime artifacts + server/docker-entrypoint.sh, db-seed/Dockerfile, db-seed/import.sh — runtime bootstrap artifacts + server/.env.example, client/.env.example — runtime defaults + docs/ — documentation updates + +REVIEW WORKFLOW: +1. Start with local contract files: AGENTS.md, prompts/general-prompt.md, the relevant prompt docs, + docs/completion-contract.md, prompts/validation-rules.md, and the active DSL slice. +2. Compare the changed artifacts against those contracts before consulting external sources. +3. Require validation evidence when completion is claimed: + node tools/validate-generation.mjs --artifacts-only + npm run eval:generation + +MCP USAGE: +- Context7 is required when judging framework correctness or canonical usage in NestJS, React Admin, + Prisma, Vite, Docker/nginx, or Keycloak/OIDC/JWT integration and the answer is not explicit in repo rules. +- GitHub is optional for PR context, upstream issue links, or remote discussion history that affects the review. +- Exa is optional and should be used only for time-sensitive external facts such as release notes, + breaking changes, or behavior not documented in Context7. +- Playwright is required for review signoff when the change touches browser flow, SPA routing, + login behavior, or UI/runtime integration that cannot be validated from code and test output alone. +- Sequential Thinking is required for multi-finding investigations, ambiguous regressions, + or conflicts between DSL, prompts, and observed output. +- Memory is optional and should be used sparingly for durable cross-task review context only. + +SOURCE PREFERENCE: +1. Root AGENTS.md and prompt contracts +2. Active DSL slice and local changed files +3. Validation output +4. Context7 official docs +5. GitHub or Exa when their specific use cases apply +""" diff --git a/.claude/config.toml b/.claude/config.toml new file mode 100644 index 0000000..6d683d6 --- /dev/null +++ b/.claude/config.toml @@ -0,0 +1,25 @@ +# Claude Code Configuration for KIS-TOiR +# +# This file mirrors `.codex/config.toml` in intent. Differences here should be +# format-level only for Claude Code compatibility, not workflow or policy drift. +# Claude and Codex use different config schemas; agent role definitions live in +# `.claude/agents/*.toml`, but the intended MCP/tool strategy matches `.codex/config.toml`. + +model = "claude-opus-4-6" + +[mcpServers] +github = { command = "npx", args = ["-y", "@modelcontextprotocol/server-github"] } +context7 = { command = "npx", args = ["-y", "@upstash/context7-mcp@latest"] } +exa = { command = "npx", args = ["-y", "@modelcontextprotocol/server-exa"] } +memory = { command = "npx", args = ["-y", "@modelcontextprotocol/server-memory"] } +playwright = { command = "npx", args = ["-y", "@playwright/mcp@latest", "--extension"] } +sequential-thinking = { command = "npx", args = ["-y", "@modelcontextprotocol/server-sequential-thinking"] } + +[agents] +explorer = { config_file = "agents/explorer.toml" } +docs_researcher = { config_file = "agents/docs-researcher.toml" } +generator_prisma = { config_file = "agents/generator_prisma.toml" } +generator_nest_resources = { config_file = "agents/generator_nest_resources.toml" } +generator_react_admin_resources = { config_file = "agents/generator_react_admin_resources.toml" } +generator_data_access = { config_file = "agents/generator_data_access.toml" } +reviewer = { config_file = "agents/reviewer.toml" } diff --git a/.codex/AGENTS.md b/.codex/AGENTS.md new file mode 100644 index 0000000..ebd582a --- /dev/null +++ b/.codex/AGENTS.md @@ -0,0 +1,181 @@ +# Codex CLI — KIS-TOiR workspace supplement + +This file supplements the repository root `AGENTS.md` with Codex-specific +operational notes. The root `AGENTS.md` is the authoritative contract — +if anything here contradicts root, root wins. + +--- + +## Agent role summary + +| Role | Config file | Sandbox | Primary responsibility | +| ----------------------------------- | --------------------------------------------- | --------------- | ---------------------- | +| `explorer` | `agents/explorer.toml` | read-only | Discovery only | +| `docs_researcher` | `agents/docs-researcher.toml` | read-only | Official docs only | +| `generator_prisma` | `agents/generator_prisma.toml` | workspace-write | Prisma schema only | +| `generator_nest_resources` | `agents/generator_nest_resources.toml` | workspace-write | Nest resource layer | +| `generator_react_admin_resources` | `agents/generator_react_admin_resources.toml` | workspace-write | React Admin resources | +| `generator_data_access` | `agents/generator_data_access.toml` | workspace-write | Frontend data access | +| `reviewer` | `agents/reviewer.toml` | read-only | Final review only | + +Use `explorer` first for discovery, `docs_researcher` for framework verification, +the specialized generators only after contract freeze, and `reviewer` only after +integration and validation. The old broad `generator` role is removed from the +normal full-generation workflow. + +--- + +## Delegation model + +- Shallow delegation only: keep `max_depth = 1` and bounded sub-tasks. +- One primary responsibility per sub-agent. +- Parent owns discovery orchestration, docs verification, contract freeze, + shared scaffold, auth platform skeleton, deploy/runtime skeleton, integration, + validation, and reviewer handoff. +- Feature/resource generation must be delegated to specialized generators before + the parent falls back to manual implementation. +- Shared seams stay parent-owned even when resource generators attach + resource-aware bindings inside their delegated zones. + +--- + +## Mutation boundary map + +``` +Tier 1 — Source of truth (NEVER written by any agent) + domain/*.api.dsl — single source of truth for all generation + prompts/*.md — generation spec / rules + AGENTS.md — agent operating rules + .codex/AGENTS.md (this file) — Codex-specific supplement + .claude/CLAUDE.md — Claude-specific supplement + +Tier 2 — Deterministic derivatives (written only by npm scripts, not by agents) + api-summary.json ← npm run generate:api-summary + openapi.json ← npm run generate:openapi (auxiliary) + +Tier 3 — LLM-generated artifacts (ownership split by bounded role) + Parent-owned shared seams: + server/src/auth/ + client/src/auth/ + toir-realm.json + docker-compose.yml + server/Dockerfile + client/Dockerfile + client/nginx/default.conf + server/docker-entrypoint.sh + db-seed/Dockerfile + db-seed/import.sh + server/.env.example + client/.env.example + + Specialized generators: + generator_prisma + server/prisma/schema.prisma + + generator_nest_resources + server/src/modules// + server/src/app.module.ts (only when explicitly delegated) + + generator_react_admin_resources + client/src/resources// + client/src/App.tsx (only when explicitly delegated) + + generator_data_access + client/src/dataProvider.ts + narrowly delegated frontend integration seams only when explicitly delegated + + Note: these Tier 3 outputs may be absent at the start of a repo-wide full + regeneration run. Their absence is expected until CLI scaffolding and + generation recreate them from Tier 1 inputs. + +Tier 4 — Framework-managed support files + framework scaffold and Prisma CLI-managed migrations outside prompt-governed outputs +``` + +## Contract freeze and acceptance + +- Parent must freeze a normalized structured contract before specialized + generation starts. +- Each delegated task must include explicit write-zones, expected outputs, and + non-goals. +- Parent accepts delegated output only if allowed zones were respected, the + frozen contract still holds, no cross-layer edits leaked, and the result is + integration-ready. +- Allow at most one bounded repair pass before explicit rejection. +- Manual fallback is allowed only after rejection, not as a silent continuation + of partial delegated work. + +## Runtime / Deploy Contract + +- Tier 3 runtime/deploy outputs are first-class generation targets and must be + regenerated or repaired from the companion rules when they drift. +- Tier 4 support files are framework-managed rather than hand-authored sources + of truth. +- Runtime/deploy readiness is part of completion, not an optional follow-up. + +Completion is defined in `docs/completion-contract.md`. + +## Version Policy + +- The approved stack version policy lives in root `AGENTS.md` and the companion prompt docs. +- After CLI scaffold creation or repair, normalize package manifests back to the approved exact versions before generation continues. +- Do not leave `latest`, caret ranges, or unreviewed major-version upgrades in regenerated manifests. +- Treat a Prisma v6 -> v7 move as an explicit migration task, not as a routine dependency refresh. + +--- + +## Standard generation invocation + +```bash +# 0. In full-regeneration mode, begin without relying on existing Tier 3 outputs +# 1. Read AGENTS.md + prompts/general-prompt.md +# 2. Use explorer for discovery and docs_researcher for official verification +# 3. Freeze the structured contract and delegated write-zones +# 4. Recreate or repair official CLI scaffolds +# 5. Launch specialized generators after contract freeze +# 6. Integrate, validate, and send to reviewer +node tools/validate-generation.mjs --artifacts-only +npm run eval:generation +``` + +--- + +## MCP servers (project-local) + +Defined in `.codex/config.toml`: + +- **github** — repository access when PR/repo context matters +- **context7** — primary library documentation lookup +- **exa** — current web search fallback +- **memory** — persistent cross-session context, sparingly +- **playwright** — browser automation only when UI/runtime verification needs it +- **sequential-thinking** — structured multi-step reasoning + +Add heavier or credential-backed servers in `~/.codex/config.toml`. + +--- + +## Validation gate + +Run before every commit and after every generation: + +```bash +# Stage 1 — structural gate +node tools/validate-generation.mjs --artifacts-only + +# Stage 2 — eval harness +npm run eval:generation +``` + +The pre-commit hook (`tools/hooks/pre-commit`) runs both stages automatically +after `npm run install-hooks`. + +Completion is defined in `docs/completion-contract.md`. + +--- + +## Security notes + +- Never commit secrets. Use environment variables from `.env.example` templates. +- Run `npm audit` when adding new dependencies to `server/` or `client/`. +- Auth contracts live in `prompts/auth-rules.md`. Do not deviate from them. diff --git a/.codex/agents/docs-researcher.toml b/.codex/agents/docs-researcher.toml new file mode 100644 index 0000000..830f5cd --- /dev/null +++ b/.codex/agents/docs-researcher.toml @@ -0,0 +1,39 @@ +model = "gpt-5.4-mini" +model_reasoning_effort = "medium" +sandbox_mode = "read-only" + +developer_instructions = """ +Verify APIs, framework behavior, and release-note claims against primary documentation before changes land. +Cite the exact docs or file paths that support each claim. +Do not invent undocumented behavior. +Start with local repository contracts first: AGENTS.md, prompts/general-prompt.md, the relevant prompt docs, +and the narrowest relevant DSL slice when the question is generation-related. + +MCP USAGE: +- Context7 is the primary source for official library/framework documentation, API references, + canonical patterns, and examples. Use it before any generic web research for NestJS, React Admin, + Prisma, Vite, Docker, nginx, Keycloak/OIDC/JWT, and other libraries used by the task. +- Before answering a framework question, first query Context7 for the exact library involved and read + the specific section relevant to the requested behavior or API. +- GitHub is optional for upstream repository context such as release discussions, issue threads, + PR conversations, or examples hosted in the project repository. +- Exa is required when the question depends on current or unstable external information that Context7 + does not reliably cover, such as release notes, breaking changes, version support, or ecosystem status. +- Playwright is optional and only relevant when documentation research needs browser evidence from a live UI flow. +- Sequential Thinking is optional for multi-step research synthesis or when multiple sources disagree. +- Memory is optional and should be used sparingly for durable research conclusions that will matter across tasks. + +SOURCE PREFERENCE: +1. Local repository contracts and DSL context +2. Context7 official docs +3. GitHub for repo-hosted context +4. Exa for current external facts + +PRE-READ REQUIREMENTS: +- NestJS questions: read the relevant NestJS docs in Context7 before answering. +- React Admin questions: read the relevant React Admin docs in Context7 before answering. +- Prisma questions: read the relevant Prisma docs in Context7 before answering. +- Vite questions: read the relevant Vite docs in Context7 before answering. +- Keycloak/OIDC/JWT questions: read the relevant official auth docs through Context7 when available; + use Exa for release-specific or deployment-specific material not covered there. +""" diff --git a/.codex/agents/explorer.toml b/.codex/agents/explorer.toml new file mode 100644 index 0000000..e88b59e --- /dev/null +++ b/.codex/agents/explorer.toml @@ -0,0 +1,48 @@ +model = "gpt-5.4-mini" +model_reasoning_effort = "medium" +sandbox_mode = "read-only" + +developer_instructions = """ +Stay in exploration mode. Read files freely; write nothing. + +Trace the real execution path, cite files and symbols, and avoid proposing +fixes unless the parent agent asks for them. +Prefer targeted search and file reads over broad scans. + +KIS-TOiR source-of-truth tier reference (read-only for this agent): + Tier 1: domain/*.api.dsl, prompts/*.md, AGENTS.md + Tier 2: api-summary.json (deterministic auxiliary derivative; never authoritative) + Tier 3: server/src/modules/, client/src/resources/, server/src/app.module.ts, + client/src/App.tsx, server/prisma/schema.prisma, server/src/auth/, + client/src/auth/, client/src/dataProvider.ts, toir-realm.json, + docker-compose.yml, server/Dockerfile, client/Dockerfile, + client/nginx/default.conf, server/docker-entrypoint.sh, + db-seed/Dockerfile, db-seed/import.sh, + server/.env.example, client/.env.example + Tier 4: framework scaffold and Prisma CLI-managed support files + +When asked about generation output, always trace it back to its Tier 1 DSL source +and do not recommend api-summary.json as the primary input when the DSL is available. + +MCP AND PRE-READ WORKFLOW: +- Start with local files first. Read AGENTS.md, prompts/general-prompt.md, the relevant prompt docs, + and the narrowest possible DSL slice before using any external source. +- Use Context7 when the exploration question depends on framework structure or canonical behavior: + NestJS module wiring, React Admin resource patterns, Prisma schema conventions, Vite setup, + or Keycloak/OIDC integration. For those questions, Context7 is required before Exa. +- Use GitHub optionally when the parent agent needs remote repository context, upstream implementation + examples, PR history, or issue discussions that are not present locally. +- Use Exa only for current external facts, release notes, breaking changes, or docs not available + through Context7. Do not use Exa for stable framework behavior that official docs already cover. +- Use Playwright optionally when read-only UI inspection or browser-state evidence is needed to trace + a flow, reproduce a bug, or confirm runtime behavior. +- Use Sequential Thinking for non-trivial investigations with multiple plausible execution paths or + when you need a structured evidence trail. Skip it for straightforward symbol lookup. +- Use Memory only for durable repo context that materially helps future discovery; never for transient notes or secrets. + +SOURCE PREFERENCE: +1. Local authoritative files and the active DSL slice +2. Local implementation files +3. Context7 official docs +4. GitHub or Exa if their specific use cases apply +""" diff --git a/.codex/agents/generator_data_access.toml b/.codex/agents/generator_data_access.toml new file mode 100644 index 0000000..74c33a1 --- /dev/null +++ b/.codex/agents/generator_data_access.toml @@ -0,0 +1,54 @@ +model = "gpt-5.4" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded frontend data-access generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate or update the frontend integration layer between React Admin and the backend contract. + +ALLOWED WRITE ZONES: +- client/src/dataProvider.ts +- narrowly delegated frontend integration seams only when the parent explicitly includes them in the frozen contract + +FORBIDDEN ZONES: +- client/src/resources/** +- client/src/auth/** unless the parent explicitly delegates a tiny compatibility touchpoint +- server/** +- server/prisma/schema.prisma +- runtime/deploy/env artifacts +- prompts, DSL files, tools, AGENTS docs + +SCOPE: +- resource/path mapping +- composite key handling +- request/response normalization +- auth-aware request plumbing according to the existing auth contract +- compatibility between backend API contract and React Admin expectations +- do not redesign frontend auth from scratch, backend auth, or shared runtime/deploy behavior + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/auth-rules.md and prompts/frontend-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official React Admin data provider and auth-related documentation +- local repository backend/path conventions next +- Exa only for version-sensitive clarification + +COMPLETION CRITERIA: +- only allowed zones changed +- data-access layer matches the frozen contract and existing auth contract +- no unauthorized resource UI, backend, or runtime redesign +- output is integration-ready for parent review + +HANDOFF EXPECTATIONS: +- report changed paths +- surface unresolved normalization or auth-plumbing issues explicitly +- do not claim ownership of final integration or validation +""" diff --git a/.codex/agents/generator_nest_resources.toml b/.codex/agents/generator_nest_resources.toml new file mode 100644 index 0000000..64587a3 --- /dev/null +++ b/.codex/agents/generator_nest_resources.toml @@ -0,0 +1,53 @@ +model = "gpt-5.4" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded NestJS resource generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate backend resource-level NestJS modules from a frozen contract. + +ALLOWED WRITE ZONES: +- server/src/modules/** +- server/src/app.module.ts only when the parent explicitly delegates module registration touchpoints + +FORBIDDEN ZONES: +- server/prisma/schema.prisma unless the parent explicitly expands the frozen contract, which should be rare +- client/** +- server/src/auth/** +- runtime/deploy/env artifacts +- prompts, DSL files, tools, AGENTS docs + +SCOPE: +- controllers +- services +- DTOs +- module-level resource wiring +- attach already-defined auth platform seams where the frozen contract requires them +- do not redesign JWT/JWKS strategy, global backend infra, or shared auth platform behavior + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/backend-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official NestJS documentation +- local repository auth/runtime seam evidence next +- Exa only when official docs are insufficient or version-sensitive details are missing + +COMPLETION CRITERIA: +- only allowed zones changed +- generated modules match the frozen contract and backend rules +- no unauthorized auth/runtime/platform redesign +- output is integration-ready for parent review + +HANDOFF EXPECTATIONS: +- report changed paths +- surface unresolved guard/decorator/wiring issues explicitly +- do not claim ownership of final integration or validation +""" diff --git a/.codex/agents/generator_prisma.toml b/.codex/agents/generator_prisma.toml new file mode 100644 index 0000000..af4aa53 --- /dev/null +++ b/.codex/agents/generator_prisma.toml @@ -0,0 +1,52 @@ +model = "gpt-5.4" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded Prisma generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate or update Prisma/data-model artifacts from a frozen contract. +- Own schema/model consistency only. + +ALLOWED WRITE ZONES: +- server/prisma/schema.prisma +- optional machine-readable schema summary only when the parent explicitly delegates it + +FORBIDDEN ZONES: +- server/src/modules/** +- client/src/resources/** +- server/src/auth/** +- client/src/auth/** +- client/src/dataProvider.ts unless the parent explicitly delegates a summary handoff there, which is discouraged +- docker-compose.yml, Dockerfiles, nginx, env templates, realm, prompts, DSL files, tools, AGENTS docs + +SCOPE: +- relations, enums, ids, composite-key representation, model consistency +- preserve or emit the Prisma header per prompt rules +- do not redesign backend/frontend/auth/runtime/platform seams + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/prisma-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official Prisma documentation +- local repository contracts next +- Exa only for version-sensitive or missing documentation details + +COMPLETION CRITERIA: +- only allowed zones changed +- schema matches the frozen contract and DSL +- no unauthorized cross-layer edits +- any parent-requested summary is included in the handoff + +HANDOFF EXPECTATIONS: +- report changed paths +- summarize any unresolved relation or migration concerns explicitly +- do not claim platform integration or validation ownership +""" diff --git a/.codex/agents/generator_react_admin_resources.toml b/.codex/agents/generator_react_admin_resources.toml new file mode 100644 index 0000000..368865b --- /dev/null +++ b/.codex/agents/generator_react_admin_resources.toml @@ -0,0 +1,53 @@ +model = "gpt-5.4" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +approval_policy = "on-request" + +developer_instructions = """ +You are the bounded React Admin resource generator for KIS-TOiR. + +ROLE / PURPOSE: +- Generate frontend resource-level React Admin UI from a frozen contract. + +ALLOWED WRITE ZONES: +- client/src/resources/** +- client/src/App.tsx only when the parent explicitly delegates resource registration touchpoints + +FORBIDDEN ZONES: +- client/src/dataProvider.ts unless the parent explicitly delegates a narrow integration touchpoint, which should usually go to generator_data_access +- client/src/auth/** +- server/** +- runtime/deploy/env artifacts +- prompts, DSL files, tools, AGENTS docs + +SCOPE: +- list/show/create/edit views +- resource-level field mapping +- form/filter/sort resource logic +- compatibility with the repository data-access and auth contracts +- do not redesign auth strategy, shared API client/data-access architecture, or runtime/platform seams + +REQUIRED PRE-READ ORDER: +1. AGENTS.md +2. prompts/general-prompt.md +3. parent-frozen structured contract +4. the narrow relevant DSL slice +5. prompts/frontend-rules.md + +PREFERRED MCP / DOC SOURCES: +- Context7 first for official React Admin documentation +- local repository contracts next +- do not rely on memory alone for React Admin patterns +- Exa only for version-sensitive clarification + +COMPLETION CRITERIA: +- only allowed zones changed +- resources match the frozen contract and frontend rules +- no unauthorized auth/data-access/runtime redesign +- output is integration-ready for parent review + +HANDOFF EXPECTATIONS: +- report changed paths +- surface unresolved resource-level compatibility issues explicitly +- do not claim ownership of shared data-access or final validation +""" diff --git a/.codex/agents/reviewer.toml b/.codex/agents/reviewer.toml new file mode 100644 index 0000000..50f275e --- /dev/null +++ b/.codex/agents/reviewer.toml @@ -0,0 +1,61 @@ +model = "gpt-5.4" +model_reasoning_effort = "high" +sandbox_mode = "read-only" + +developer_instructions = """ +Review mode. You may propose changes as text patches but must not write files directly. + +Focus on: +- Correctness: does generated code match the api.dsl and prompt contracts? +- Security: auth guard placement, CORS, env variable handling. +- Regression: do both verification gates pass? + node tools/validate-generation.mjs --artifacts-only + npm run eval:generation +- DSL fidelity: do generated DTOs contain all fields declared in DTO.Create/Update? +- Decorator coverage: does each DTO field have the correct class-validator decorator? +- Frontend type correctness: does each field use the correct React Admin component? +- Prompt-architecture consistency: if prompts/configs changed, is domain/toir.api.dsl still clearly authoritative and api-summary.json still clearly auxiliary? + +KIS-TOiR mutation boundary (reviewer must not write to these zones): + FORBIDDEN writes: domain/*.api.dsl, prompts/*.md, AGENTS.md, + api-summary.json, tools/, server/prisma/schema.prisma + + ALLOWED proposal targets (propose patches, not direct writes): + server/src/modules// — backend artifacts + client/src/resources// — frontend artifacts + server/src/app.module.ts, client/src/App.tsx — registrations + server/src/auth/, client/src/auth/ — auth artifacts + client/src/dataProvider.ts — authenticated data provider seam + toir-realm.json, docker-compose.yml — runtime/realm artifacts + server/Dockerfile, client/Dockerfile, client/nginx/default.conf — deploy/runtime artifacts + server/docker-entrypoint.sh, db-seed/Dockerfile, db-seed/import.sh — runtime bootstrap artifacts + server/.env.example, client/.env.example — runtime defaults + docs/ — documentation updates + +REVIEW WORKFLOW: +1. Start with local contract files: AGENTS.md, prompts/general-prompt.md, the relevant prompt docs, + docs/completion-contract.md, prompts/validation-rules.md, and the active DSL slice. +2. Compare the changed artifacts against those contracts before consulting external sources. +3. Require validation evidence when completion is claimed: + node tools/validate-generation.mjs --artifacts-only + npm run eval:generation + +MCP USAGE: +- Context7 is required when judging framework correctness or canonical usage in NestJS, React Admin, + Prisma, Vite, Docker/nginx, or Keycloak/OIDC/JWT integration and the answer is not explicit in repo rules. +- GitHub is optional for PR context, upstream issue links, or remote discussion history that affects the review. +- Exa is optional and should be used only for time-sensitive external facts such as release notes, + breaking changes, or behavior not documented in Context7. +- Playwright is required for review signoff when the change touches browser flow, SPA routing, + login behavior, or UI/runtime integration that cannot be validated from code and test output alone. +- Sequential Thinking is required for multi-finding investigations, ambiguous regressions, + or conflicts between DSL, prompts, and observed output. +- Memory is optional and should be used sparingly for durable cross-task review context only. + +SOURCE PREFERENCE: +1. Root AGENTS.md and prompt contracts +2. Active DSL slice and local changed files +3. Validation output +4. Context7 official docs +5. GitHub or Exa when their specific use cases apply +""" diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..e3d45ef --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,137 @@ +#:schema https://developers.openai.com/codex/config-schema.json + +# Everything Claude Code (ECC) — Codex Reference Configuration +# +# Copy this file to ~/.codex/config.toml for global defaults, or keep it in +# the project root as .codex/config.toml for project-local settings. +# +# Official docs: +# - https://developers.openai.com/codex/config-reference +# - https://developers.openai.com/codex/multi-agent + +# Model selection +# Leave `model` and `model_provider` unset so Codex CLI uses its current +# built-in defaults. Uncomment and pin them only if you intentionally want +# repo-local or global model overrides. + +# Top-level runtime settings (current Codex schema) +approval_policy = "on-request" +sandbox_mode = "workspace-write" +web_search = "live" + +# External notifications receive a JSON payload on stdin. +# macOS example (uncomment on Mac if `terminal-notifier` is installed): +# notify = [ +# "terminal-notifier", +# "-title", "Codex KIS", +# "-message", "Task completed!", +# "-sound", "default", +# ] + +# Persistent instructions are appended to every prompt (additive, unlike +# model_instructions_file which replaces AGENTS.md). +persistent_instructions = "Follow project AGENTS.md guidelines. Use available MCP servers when they can help." + +# model_instructions_file replaces built-in instructions instead of AGENTS.md, +# so leave it unset unless you intentionally want a single override file. +# model_instructions_file = "/absolute/path/to/instructions.md" + +# MCP servers +# Keep the default project set lean. API-backed servers inherit credentials from +# the launching environment or can be supplied by a user-level ~/.codex/config.toml. +[mcp_servers.github] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-github"] +startup_timeout_sec = 30 + +[mcp_servers.context7] +command = "npx" +# Canonical Codex section name is `context7`; the package itself remains +# `@upstash/context7-mcp`. +args = ["-y", "@upstash/context7-mcp@latest"] +startup_timeout_sec = 30 + +[mcp_servers.exa] +url = "https://mcp.exa.ai/mcp" + +[mcp_servers.memory] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-memory"] +startup_timeout_sec = 30 + +[mcp_servers.playwright] +command = "npx" +args = ["-y", "@playwright/mcp@latest"] +startup_timeout_sec = 30 + +[mcp_servers.sequential-thinking] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-sequential-thinking"] +startup_timeout_sec = 30 + +# Additional MCP servers (uncomment as needed): +# [mcp_servers.supabase] +# command = "npx" +# args = ["-y", "supabase-mcp-server@latest", "--read-only"] +# +# [mcp_servers.firecrawl] +# command = "npx" +# args = ["-y", "firecrawl-mcp"] +# +# [mcp_servers.fal-ai] +# command = "npx" +# args = ["-y", "fal-ai-mcp-server"] +# +# [mcp_servers.cloudflare] +# command = "npx" +# args = ["-y", "@cloudflare/mcp-server-cloudflare"] + +[features] +# Codex multi-agent collaboration is stable and on by default in current builds. +# Keep the explicit toggle here so the repo documents its expectation clearly. +multi_agent = true + +# Profiles — switch with `codex -p ` +[profiles.strict] +approval_policy = "on-request" +sandbox_mode = "read-only" +web_search = "cached" + +[profiles.yolo] +approval_policy = "never" +sandbox_mode = "workspace-write" +web_search = "live" + +[agents] +# Multi-agent role limits and local role definitions. +# These map to `.codex/agents/*.toml` and mirror the repo's explorer/reviewer/docs workflow. +max_threads = 6 +max_depth = 1 + +[agents.explorer] +description = "Read-only codebase explorer for gathering evidence before changes are proposed." +config_file = "agents/explorer.toml" + +[agents.reviewer] +description = "PR reviewer focused on correctness, security, and DSL fidelity. Proposes patches; writes nothing directly." +config_file = "agents/reviewer.toml" + +[agents.docs_researcher] +description = "Documentation specialist that verifies APIs, framework behavior, and release notes." +config_file = "agents/docs-researcher.toml" + +[agents.generator_prisma] +description = "Bounded Prisma schema generator for frozen-contract data-model work only." +config_file = "agents/generator_prisma.toml" + +[agents.generator_nest_resources] +description = "Bounded NestJS resource generator for server module artifacts only." +config_file = "agents/generator_nest_resources.toml" + +[agents.generator_react_admin_resources] +description = "Bounded React Admin resource generator for frontend resource artifacts only." +config_file = "agents/generator_react_admin_resources.toml" + +[agents.generator_data_access] +description = "Bounded frontend data-access generator for the React Admin/backend integration seam." +config_file = "agents/generator_data_access.toml" diff --git a/.env.portainer.example b/.env.portainer.example new file mode 100644 index 0000000..bbde392 --- /dev/null +++ b/.env.portainer.example @@ -0,0 +1,13 @@ +POSTGRES_USER=postgres +POSTGRES_PASSWORD=change-me +POSTGRES_DB=toir +CORS_ALLOWED_ORIGINS=https://toir.example.ru + +KEYCLOAK_ISSUER_URL=https://sso.example.ru/realms/toir +KEYCLOAK_AUDIENCE=toir-backend +KEYCLOAK_JWKS_URL= + +VITE_API_URL=/api +VITE_KEYCLOAK_URL=https://sso.example.ru +VITE_KEYCLOAK_REALM=toir +VITE_KEYCLOAK_CLIENT_ID=toir-frontend diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58f091b --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Dependencies +**/node_modules/ + +# Build outputs +**/dist/ +**/dist-ssr/ +**/coverage/ +**/.cache/ +**/*.tsbuildinfo + +# Environment files +**/.env +**/.env.local +**/.env.*.local + +# Logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# OS files +.DS_Store +Thumbs.db + +# Editor / IDE +.vscode/* +!.vscode/extensions.json +.idea/ +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +# Generated OpenAPI (local runs; commit only if you want to publish the spec) +openapi.generated.json +openapi.llm.json +tools/api-format-to-openapi/demo-output/ + +.cursor/ \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6403d24 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,475 @@ +# KIS-TOiR — Agent Operating Rules + +Read this file at the start of every session before reading any other file. + +--- + +## What this repository is + +KIS-TOiR is a fullstack CRUD application (NestJS backend + React Admin frontend) +for equipment maintenance management (Техническое обслуживание и ремонт). + +Generation is driven by a single authoritative source file: + +- `domain/toir.api.dsl` — the complete API contract: enums, DTOs, endpoints, HTTP methods, pagination + +--- + +## Source-of-truth hierarchy + +### Tier 1 — authoritative (hand-authored; never overwritten by generation) + +| File | Authoritative for | +| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `domain/*.api.dsl` | Enums, DTO shapes per operation, nullability, HTTP verb+path per endpoint, endpoint names, pagination contracts. Single source of truth. Drives: NestJS modules + React Admin resources + Prisma schema. | +| `prompts/*.md` | Auth rules, runtime rules, framework scaffold rules, Prisma rules, validation rules, generation policy, naming conventions. | +| `AGENTS.md` (this file) | Agent workflow, mutation boundaries, verification contract. | +| `docs/completion-contract.md` | Operational definition of done, success tiers, failure modes, recovery rules. | +| `.codex/AGENTS.md` | Codex-specific agent governance supplement. | +| `.claude/CLAUDE.md` | Claude-specific agent governance supplement. | + +### Tier 2 — deterministic derivative (generated by script; never edited manually) + +| File | Generated from | Command | +| ------------------ | ------------------ | ------------------------------ | +| `api-summary.json` | `domain/*.api.dsl` | `npm run generate:api-summary` | + +### Tier 3 — LLM-generated artifacts (never edit manually after generation) + +These outputs are end-state generation targets. During a repo-wide full +regeneration driven by `prompts/general-prompt.md`, they may be absent at the +start of the run and are recreated from Tier 1 sources plus official CLI +scaffolding. + +| Zone | Generated from | +| -------------------------------- | ------------------------------------------------------ | +| `server/src/modules//` | `domain/*.api.dsl` + `prompts/backend-rules.md` | +| `client/src/resources//` | `domain/*.api.dsl` + `prompts/frontend-rules.md` | +| `server/src/app.module.ts` | Module registrations derived from api.dsl api blocks | +| `client/src/App.tsx` | Resource registrations derived from api.dsl api blocks | +| `server/prisma/schema.prisma` | `domain/*.api.dsl` + `prompts/prisma-rules.md` | +| `server/src/auth/` | `prompts/auth-rules.md` | +| `client/src/auth/` | `prompts/auth-rules.md` | +| `client/src/dataProvider.ts` | `prompts/auth-rules.md` | +| `toir-realm.json` | `prompts/auth-rules.md` | +| `docker-compose.yml` | `prompts/runtime-rules.md` | +| `server/Dockerfile` | `prompts/runtime-rules.md` | +| `client/Dockerfile` | `prompts/runtime-rules.md` | +| `client/nginx/default.conf` | `prompts/runtime-rules.md` | +| `server/docker-entrypoint.sh` | `prompts/runtime-rules.md` | +| `db-seed/Dockerfile` | `prompts/runtime-rules.md` | +| `db-seed/import.sh` | `prompts/runtime-rules.md` | +| `server/.env.example` | `prompts/runtime-rules.md` | +| `client/.env.example` | `prompts/runtime-rules.md` | + +### Tier 4 — handwritten / framework-managed support files + +- Framework scaffold and bootstrap helpers that remain manually maintained unless a repair task says otherwise: + `server/nest-cli.json`, `server/tsconfig*.json`, `client/vite.config.*`, etc. + +### Runtime / Deploy Contract + +- Tier 3 runtime/deploy outputs are first-class generation targets and must be regenerated from the companion rules when they drift. +- Tier 4 support files are framework-managed rather than hand-authored sources of truth. +- Runtime/deploy readiness is part of completion, not an optional follow-up. + +## Approved Stack Version Policy + +Future full-regeneration runs must normalize scaffolded package manifests and runtime inputs to the approved versions below. Do not use `latest`, caret ranges, or unreviewed major bumps when the repository regenerates or repairs the stack. + +Runtime baseline: + +- Node.js: prefer the Node 22 LTS line; minimum approved version is `22.12.0` +- Package manager: `npm` only, with committed `package-lock.json` files; do not switch the repo to `pnpm`, `yarn`, or `bun` + +Backend generation baseline: + +- `@nestjs/common`, `@nestjs/core`, `@nestjs/platform-express`, `@nestjs/testing`: `11.1.18` +- `@nestjs/config`: `4.0.3` +- `@nestjs/cli`: `11.0.17` +- `@nestjs/schematics`: `11.0.10` +- `prisma` and `@prisma/client`: `6.16.2` exactly, kept on the same version +- `class-validator`: `0.15.1` +- `class-transformer`: `0.5.1` +- `jose`: `6.2.2` +- `reflect-metadata`: `0.2.2` +- `rxjs`: `7.8.1` +- backend `typescript`: `5.7.3` + +Frontend generation baseline: + +- `react` and `react-dom`: `19.2.4` +- `react-admin` and `ra-data-simple-rest`: `5.14.5` +- `@mui/material` and `@mui/icons-material`: `7.3.9` +- `@emotion/react`: `11.14.0` +- `@emotion/styled`: `11.14.1` +- `vite`: `8.0.3` +- `@vitejs/plugin-react`: `6.0.1` +- frontend `typescript`: `5.9.3` +- `keycloak-js`: `26.2.3` + +Policy rules: + +- When official CLIs scaffold newer dependency sets, repair the scaffold and then pin it back to the approved versions above before generation continues. +- `prisma` and `@prisma/client` must always stay on the same exact version. +- Do not upgrade Prisma from v6 to v7 as part of routine regeneration; treat that as a separate explicit migration task with updated prompts, runtime checks, and verification. + +--- + +## Forbidden mutations during any generation run + +**NEVER write to `*.dsl` files.** +They are read-only inputs. To change the API contract or domain model, edit `domain/*.api.dsl` +as a separate explicit task. + +**NEVER manually edit files under `server/src/modules/` or `client/src/resources/`.** +To change generated code: update `domain/*.api.dsl` and regenerate. + +**NEVER edit `server/prisma/schema.prisma` directly.** +It is LLM-generated from `domain/*.api.dsl` following `prompts/prisma-rules.md`. + +--- + +## Multi-agent orchestration model + +Full-generation runs use a parent-orchestrated, bounded multi-agent model. +The parent agent is the orchestrator/integrator. It is not the default broad +feature implementer. + +Parent-only responsibilities: + +- discovery orchestration +- docs verification orchestration +- contract freeze +- shared platform scaffold +- auth platform skeleton +- deploy/runtime skeleton +- shared platform wiring and env/runtime conventions +- launching specialized generators +- accepting or rejecting generator outputs +- final integration +- validation +- final handoff to reviewer + +Specialized generation agents: + +- `generator_prisma` — schema/model generation only +- `generator_nest_resources` — NestJS resource modules only +- `generator_react_admin_resources` — React Admin resource UI only +- `generator_data_access` — frontend data-access seam only + +The old universal `generator` agent is removed from the active orchestration +model and must not be used for full-generation workflows. + +### Delegation order + +Use agents in this order: + +1. `explorer` for repository discovery, execution-path inspection, scaffold checks, and local seam tracing +2. `docs_researcher` for official documentation verification and version-sensitive framework behavior +3. parent contract freeze and shared scaffold/auth/runtime planning +4. specialized generators after contract freeze, in parallel when safe: + - `generator_prisma` + - `generator_nest_resources` + - `generator_react_admin_resources` + - `generator_data_access` +5. parent integration and validation +6. `reviewer` only at the final review stage + +If a runtime does not expose named subagents, keep the same separation of +responsibilities inside one agent and preserve the same handoff boundaries. + +### Write-zone ownership + +Parent-only write zones: + +- shared scaffold and framework repair surfaces +- `server/src/auth/` +- `client/src/auth/` +- `toir-realm.json` +- `docker-compose.yml` +- `server/Dockerfile` +- `client/Dockerfile` +- `client/nginx/default.conf` +- `server/docker-entrypoint.sh` +- `db-seed/Dockerfile` +- `db-seed/import.sh` +- `server/.env.example` +- `client/.env.example` +- shared integration seams and final validation outputs explicitly delegated by prompts + +Specialized generator write zones: + +- `generator_prisma` + - `server/prisma/schema.prisma` + - optional machine-readable schema summary or structured handoff artifact when the parent explicitly requests it +- `generator_nest_resources` + - `server/src/modules/**` + - backend registration touchpoints only when explicitly delegated by the parent contract +- `generator_react_admin_resources` + - `client/src/resources/**` + - frontend resource registration touchpoints only when explicitly delegated by the parent contract +- `generator_data_access` + - `client/src/dataProvider.ts` + - narrowly delegated frontend integration seams only when explicitly delegated by the parent contract + +Specialized generators must not redesign shared auth, runtime, deploy, scaffold, +or platform seams outside their delegated zones. + +### Contract freeze (required before specialized generation) + +Before launching specialized generators, the parent must produce a normalized +structured handoff from the DSL and prompt contracts. This contract freeze is a +required protocol owned by the parent; it does not replace `domain/*.api.dsl`. + +The frozen contract must cover, where relevant: + +- entities +- fields +- scalar and enum types +- ids and composite keys +- relations +- enums +- endpoint conventions +- route and path conventions +- naming rules +- auth surface expectations +- validator and eval compatibility constraints +- allowed write-zones per generator + +Specialized generators start only after the parent freezes this contract and +delegates a bounded write-scope. + +### Acceptance protocol for generator outputs + +Parent acceptance is explicit, never implicit. A delegated output is accepted +only if all of the following are true: + +- only allowed zones were modified +- the frozen contract is respected +- there are no unauthorized cross-layer edits +- the output is integration-ready +- relevant validation or build checks were attempted where applicable +- unresolved issues are surfaced explicitly + +Failure handling: + +- allow at most one bounded repair pass for a rejected generator output +- reject explicitly if the output is still not usable after that pass +- use manual fallback only after explicit rejection, never as a silent completion of half-finished delegated work + +### Role guidance + +- `explorer` + - use first for codebase discovery, file/symbol tracing, scaffold inspection, and local evidence gathering +- `docs_researcher` + - use for official documentation verification, framework semantics, and version-sensitive claims +- `generator_prisma` + - launch after contract freeze when schema/model artifacts need generation or repair +- `generator_nest_resources` + - launch after contract freeze when backend resource modules need generation or repair +- `generator_react_admin_resources` + - launch after contract freeze when frontend resource views and registrations need generation or repair +- `generator_data_access` + - launch after contract freeze when React Admin to backend integration seams need generation or repair +- `reviewer` + - use only after parent integration and validation, as the final correctness/security/test-gap review gate + +### Documentation-first rule for parent planning + +Before the parent designs or repairs framework-sensitive shared seams, it must +review current official documentation rather than relying on memory alone. + +Use Context7 first for: + +- React Admin authProvider and dataProvider expectations +- NestJS modules, controllers, providers, guards, and auth attachment patterns +- Prisma schema and relation behavior +- Vite, Docker, and nginx behavior relevant to scaffold/runtime work +- Keycloak, OIDC, JWT, and JWKS integration guidance when available + +Use external web research only for current, unstable, or missing documentation +details. Parent auth, data-access, and runtime planning must not be done purely +from memory when framework-specific guidance matters. + +--- + +## Generation workflow (required sequence) + +0. For a repo-wide full regeneration driven by `prompts/general-prompt.md`, start from the Tier 1/Tier 2 contract without pre-existing Tier 3 app/runtime outputs. In that mode, `server/`, `client/`, `db-seed/`, and root runtime artifacts such as `docker-compose.yml` and `toir-realm.json` are recreated; they are not prerequisites. +1. Read `AGENTS.md` and `prompts/general-prompt.md`. +2. Run discovery first with `explorer` and verify framework-sensitive behavior with `docs_researcher` before designing shared seams. +3. Read the active `api API.` block + its DTOs + its enums from `domain/toir.api.dsl` (entity-scoped — do not inject the full DSL as a blob). +4. Load the companion rule files required for the active stage: + - `prompts/prisma-rules.md` + - `prompts/backend-rules.md` + - `prompts/frontend-rules.md` + - `prompts/auth-rules.md` + - `prompts/runtime-rules.md` + - `prompts/validation-rules.md` +5. Freeze a normalized structured contract and bounded write-zones before launching any specialized generator. +6. Verify or repair framework scaffolds before domain generation: + - official Nest CLI for backend workspace creation/repair + - official Vite React TypeScript CLI for frontend workspace creation/repair + - official Prisma CLI when Prisma initialization or repair is required +7. Parent generates shared auth platform skeleton and deploy/runtime skeleton per `prompts/auth-rules.md` and `prompts/runtime-rules.md`. +8. Launch specialized generators after contract freeze, in parallel when safe: + - `generator_prisma` for `server/prisma/schema.prisma` + - `generator_nest_resources` for backend resource modules + - `generator_react_admin_resources` for frontend resource modules + - `generator_data_access` for `client/src/dataProvider.ts` +9. Accept or reject each delegated output using the acceptance protocol above. Only after acceptance may the parent integrate results. +10. Refresh `api-summary.json` only when validation/tooling requires the auxiliary freshness artifact: `npm run generate:api-summary`. +11. Run: `node tools/validate-generation.mjs --artifacts-only` +12. Run: `npm run eval:generation` +13. Fix all failures before considering the task complete. +14. Hand the final integrated result to `reviewer` before claiming completion. + +**Context budget rule:** Before generating any DTO or component, quote the field +definitions from the DSL api block verbatim, then generate from those quotes. This +prevents training-data contamination. See `prompts/general-prompt.md`. + +--- + +## Type mappings + +| DSL type | Prisma type | TS DTO type | React Admin component | +| --------- | ----------------------------- | ----------- | ----------------------------- | +| `uuid` | `String @id @default(uuid())` | `string` | `TextInput` / `TextField` | +| `string` | `String` | `string` | `TextInput` / `TextField` | +| `text` | `String` | `string` | `TextInput` / `TextField` | +| `integer` | `Int` | `number` | `NumberInput` / `NumberField` | +| `number` | `Float` | `number` | `NumberInput` / `NumberField` | +| `decimal` | `Decimal` | `string` | `NumberInput` / `NumberField` | +| `date` | `DateTime` | `string` | `DateInput` / `DateField` | +| `boolean` | `Boolean` | `boolean` | (appropriate boolean input) | +| enum name | enum name | `string` | `SelectInput` / `SelectField` | + +--- + +## Naming conventions + +Resource name (plural, kebab-case): + +- `Equipment` → `equipment` (irregular — stays as-is) +- `EquipmentType` → `equipment-types` +- `RepairOrder` → `repair-orders` +- General: PascalCase → kebab-case → append `s` (or `es` if ends in `s`; irregular cases explicit) + +Default sort field (when not declared in api.dsl): +Priority: `inventoryNumber` > `number` > `code` > `name` > primary key + +--- + +## Verification gate (two-stage) + +### Stage 1 — Structural gate + +``` +node tools/validate-generation.mjs --artifacts-only +``` + +Checks: scaffold/build readiness, required runtime/deploy artifact presence, api-summary freshness, auth seam contracts, realm structure, env/runtime contract, Docker/nginx/compose invariants, and whether build verification ran or was explicitly skipped. +This gate validates the post-generation repository state. It is not a prerequisite for the clean-slate start of a full regeneration run. + +Full structural verification (requires installed `node_modules`): + +``` +node tools/validate-generation.mjs +``` + +### Stage 2 — Eval harness (Rule 6) + +``` +npm run eval:generation +``` + +Fixture-based semantic checks from `tools/eval/fixtures/`. Checks: DSL fidelity, CRUD method/path behavior, DTO field coverage and decorators, natural-key semantics, FK/reference wiring, Content-Range behavior, and React Admin UX/component invariants. + +Reviewed eval fixtures are the authoritative semantic gate. They may be scaffolded from source-of-truth as a helper, but a full regeneration run must not auto-rewrite the committed eval corpus as part of step 0. + +See `tools/eval/README.md` for fixture authoring and eval-driven development workflow. + +**Generation is incomplete unless both stages pass.** + +--- + +## Proven-Good Runtime References + +Existing runnable runtime/deploy artifacts are baseline behavior, not disposable examples. + +- Preserve repository-proven behavior in `docker-compose.yml`, `server/Dockerfile`, `client/Dockerfile`, `client/nginx/default.conf`, `server/.env.example`, and `client/.env.example` unless the DSL, prompt contract, or an explicit task requires a change. +- When these artifacts are regenerated, preserve still-valid production behavior such as SPA routing, `/api` proxying, external Keycloak, PostgreSQL topology, and container startup flow. +- Do not claim runtime/deploy readiness from file presence alone; these artifacts remain part of completion and verification. + +## Prisma Migration Policy + +Current repository policy: + +- `server/prisma/schema.prisma` is generated from the DSL and is the immediate database contract for generation runs. +- Committed Prisma migrations are preferred but not currently required for every schema change. +- The current repository state is accepted temporary technical debt: when `server/prisma/migrations/` is absent, local/runtime flows may fall back to `prisma db push`; when migrations exist, runtime must use `prisma migrate deploy`. + +Target policy: + +- Every DSL-driven schema change should produce a reviewed committed migration under `server/prisma/migrations/`. +- Production/runtime execution should rely on `prisma migrate deploy` only, with the `db push` fallback removed after migration discipline is established. + +## Pre-commit hook + +Install with `npm run install-hooks`. The hook runs **both** the structural gate and +the eval harness before every commit. Commits are blocked when either fails. + +--- + +## Auth and runtime defaults + +Full auth contract: `prompts/auth-rules.md` + +Working defaults (do not regress to localhost): + +- Keycloak base: `https://sso.greact.ru` +- Realm/client: `toir` / `toir-frontend` / `toir-backend` +- Production frontend: `https://toir-frontend.greact.ru` +- CORS: `http://localhost:5173,https://toir-frontend.greact.ru` + +--- + +## OpenRouter configuration + +``` +OPENAI_API_KEY= +OPENAI_BASE_URL=https://openrouter.ai/api/v1 +OPENAI_MODEL= +``` + +These variables are used by `tools/api-format-to-openapi/convert.mjs --mode llm`. + +--- + +## Reading order for generation tasks + +**Critical zone (load first, never drop):** + +1. `AGENTS.md` (this file) — project governance, mutation boundaries, tier hierarchy +2. `prompts/general-prompt.md` — master orchestration prompt: mission, stage ownership, delegation model, completion criteria +3. `domain/toir.api.dsl §API.` — active api block only, plus its referenced DTOs and enums + +**Companion zone (load when the matching stage is active):** + +4. `prompts/prisma-rules.md` — Prisma schema generation details +5. `prompts/backend-rules.md` — NestJS generation details +6. `prompts/frontend-rules.md` — React Admin generation details +7. `prompts/auth-rules.md` — auth seam and realm requirements +8. `prompts/runtime-rules.md` — scaffold, env, and bootstrap requirements +9. `prompts/validation-rules.md` — success gate requirements + +**Auxiliary zone (never authoritative):** + +10. `api-summary.json` — optional inventory/freshness artifact for validators and supporting tooling; do not use it instead of the DSL + +**Reference only (do not load proactively):** + +- `domain/dsl-spec.md` — DSL syntax reference; load only if DSL is ambiguous +- `docs/generation-playbook.md` — step-by-step workflow reference +- `docs/future-work.md` — deferred items (Rules 7 and 8) diff --git a/README.md b/README.md new file mode 100644 index 0000000..19a24b4 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +This repository keeps the current LLM-first CRUD generation architecture as the primary working baseline. + +It is not a new generator engine and it is not a compiler platform. The repository remains: + +- an AI generation context +- an active generated and maintained fullstack CRUD project +- `server/` as the active backend target output path +- `client/` as the active frontend target output path +- an LLM-first orchestration baseline with CLI-first framework bootstrap +- a compact rule set that strengthens the existing pipeline with: + - `api-summary.json` (deterministic intermediate context) + - a physical root-level realm artifact + - a lightweight automated validation gate + +## Active knowledge blocks + +The master generation prompt is `prompts/general-prompt.md`. It contains the complete +generation workflow, type mappings, naming conventions, and all core rules. + +Companion rule files for artifact-specific details: + +1. [prompts/general-prompt.md](prompts/general-prompt.md) — master generation prompt +2. [prompts/auth-rules.md](prompts/auth-rules.md) — auth seam / realm spec +3. [prompts/backend-rules.md](prompts/backend-rules.md) — backend reference +4. [prompts/frontend-rules.md](prompts/frontend-rules.md) — frontend reference +5. [prompts/prisma-rules.md](prompts/prisma-rules.md) — Prisma schema rules +6. [prompts/runtime-rules.md](prompts/runtime-rules.md) — runtime / bootstrap +7. [prompts/validation-rules.md](prompts/validation-rules.md) — validation gate + +## Baseline contracts + +- `domain/*.api.dsl` is the single source of truth for the domain model and API contract. +- [api-summary.json](api-summary.json) is a derived artifact for LLM stabilization and validation. +- [toir-realm.json](toir-realm.json) is the physical Keycloak bootstrap artifact baseline. +- `server/` and `client/` are the active target output paths for this repository. +- `server/` must remain a valid NestJS workspace baseline. +- `client/` must remain a valid Vite React TypeScript workspace baseline. + +## Scaffold baseline + +- Generation remains LLM-first for orchestration and domain-derived feature code. +- Framework bootstrap is CLI-first: + - backend baseline starts from official Nest CLI conventions + - frontend baseline starts from official Vite React TypeScript conventions +- If `server/` or `client/` drift away from a valid workspace, repair the workspace baseline before generating more feature code. +- Do not replace the framework workspace with a hand-written minimal skeleton. + +## Anti-regression contract + +- The active prompts define forbidden generation patterns, required invariants, and recovery rules for future agents. +- Buildability is part of the baseline contract, not an optional follow-up. +- Validation targets `domain/*.api.dsl` as reusable source inputs, while TOiR names remain project defaults/examples. + +## Repository layout + +- [docs/repository-structure.md](docs/repository-structure.md) explains the normalized folder structure. +- Active prompts live in `prompts/`. +- Helper scripts live in `tools/`. + +## Commands + +```bash +npm run generate:api-summary +npm run validate:generation +npm run validate:generation:runtime +npm run eval:generation +``` + +`npm run validate:generation` checks both contract shape and workspace validity. When dependencies are installed, it also verifies `npm run build` in `server/` and `client/`. If dependencies are missing, it reports build verification as skipped instead of pretending the baseline is fully green. + +## AID export (OpenAPI) + +HTTP-экспортёр для интеграции с AID: **`POST /aid/export/openapi`** (api-format → OpenAPI 3.0). Подробно: **[docs/AID_EXPORT_README.md](docs/AID_EXPORT_README.md)**. + +> **Note:** The `POST /aid/export/app` endpoint (DSL → generated app bundle) is currently +> non-operative because its backing script (`generation/generate.mjs`) was removed during +> the architecture migration to api.dsl-first generation. See `docs/AID_EXPORT_README.md` +> for details. diff --git a/api-summary.json b/api-summary.json new file mode 100644 index 0000000..b808e4f --- /dev/null +++ b/api-summary.json @@ -0,0 +1,780 @@ +{ + "sourceFiles": [ + "domain/toir.api.dsl" + ], + "enums": [], + "dtos": [ + { + "name": "DTO.Equipment", + "description": "Полный response-объект для единицы оборудования", + "fields": [ + { + "name": "id", + "type": "uuid", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": null, + "map": "Equipment.id", + "sync": false, + "label": null + }, + { + "name": "name", + "type": "string", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": "Название оборудования", + "map": "Equipment.name", + "sync": false, + "label": null + }, + { + "name": "serialNumber", + "type": "string", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": "Заводской (серийный) номер", + "map": "Equipment.serialNumber", + "sync": false, + "label": null + }, + { + "name": "dateOfInspection", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Дата поверки", + "map": "Equipment.dateOfInspection", + "sync": false, + "label": null + }, + { + "name": "commissionedAt", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Дата изготовления", + "map": "Equipment.commissionedAt", + "sync": false, + "label": null + }, + { + "name": "status", + "type": "EquipmentStatus", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": "Текущий статус", + "map": "Equipment.status", + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.EquipmentCreate", + "description": "Тело запроса на создание единицы оборудования", + "fields": [ + { + "name": "name", + "type": "string", + "required": true, + "nullable": false, + "unique": false, + "primary": false, + "description": "Название оборудования", + "map": "Equipment.name", + "sync": false, + "label": null + }, + { + "name": "serialNumber", + "type": "string", + "required": true, + "nullable": false, + "unique": false, + "primary": false, + "description": "Заводской (серийный) номер", + "map": "Equipment.serialNumber", + "sync": false, + "label": null + }, + { + "name": "dateOfInspection", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Дата поверки", + "map": "Equipment.dateOfInspection", + "sync": false, + "label": null + }, + { + "name": "commissionedAt", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Дата изготовления", + "map": "Equipment.commissionedAt", + "sync": false, + "label": null + }, + { + "name": "status", + "type": "EquipmentStatus", + "required": true, + "nullable": false, + "unique": false, + "primary": false, + "description": "Текущий статус", + "map": "Equipment.status", + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.EquipmentUpdate", + "description": "Тело запроса на обновление единицы оборудования", + "fields": [ + { + "name": "name", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Название оборудования", + "map": "Equipment.name", + "sync": false, + "label": null + }, + { + "name": "serialNumber", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Заводской (серийный) номер", + "map": "Equipment.serialNumber", + "sync": false, + "label": null + }, + { + "name": "dateOfInspection", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Дата поверки", + "map": "Equipment.dateOfInspection", + "sync": false, + "label": null + }, + { + "name": "commissionedAt", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Дата изготовления", + "map": "Equipment.commissionedAt", + "sync": false, + "label": null + }, + { + "name": "status", + "type": "EquipmentStatus", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Текущий статус", + "map": "Equipment.status", + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.EquipmentListRequest", + "description": "Запрос для постраничного получения списка оборудования с фильтрацией", + "fields": [ + { + "name": "page", + "type": "DTO.PageRequest", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "filterName", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "filterSerialNumber", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "filterStatus", + "type": "EquipmentStatus", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.EquipmentListResponse", + "description": "Ответ с постраничным списком оборудования и метаданными", + "fields": [ + { + "name": "content", + "type": "DTO.Equipment[]", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "pageInfo", + "type": "DTO.PageInfo", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.ChangeEquipmentStatus", + "description": "Полный response-объект для документа изменения статуса", + "fields": [ + { + "name": "equipmentId", + "type": "Equipment", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": "ChangeEquipmentStatus.equipmentId", + "sync": false, + "label": null + }, + { + "name": "newStatus", + "type": "EquipmentStatus", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": "Новый статус", + "map": "ChangeEquipmentStatus.newStatus", + "sync": false, + "label": null + }, + { + "name": "number", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Номер", + "map": "ChangeEquipmentStatus.number", + "sync": false, + "label": null + }, + { + "name": "date", + "type": "date", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": "Дата изменения статуса", + "map": "ChangeEquipmentStatus.date", + "sync": false, + "label": null + }, + { + "name": "responsible", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Ответственный", + "map": "ChangeEquipmentStatus.responsible", + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.ChangeEquipmentStatusCreate", + "description": "Тело запроса на создание документа изменения статуса", + "fields": [ + { + "name": "equipmentId", + "type": "Equipment", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": "ChangeEquipmentStatus.equipmentId", + "sync": false, + "label": null + }, + { + "name": "newStatus", + "type": "EquipmentStatus", + "required": true, + "nullable": false, + "unique": false, + "primary": false, + "description": "Новый статус", + "map": "ChangeEquipmentStatus.newStatus", + "sync": false, + "label": null + }, + { + "name": "number", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Номер", + "map": "ChangeEquipmentStatus.number", + "sync": false, + "label": null + }, + { + "name": "date", + "type": "date", + "required": true, + "nullable": false, + "unique": false, + "primary": false, + "description": "Дата изменения статуса", + "map": "ChangeEquipmentStatus.date", + "sync": false, + "label": null + }, + { + "name": "responsible", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Ответственный", + "map": "ChangeEquipmentStatus.responsible", + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.ChangeEquipmentStatusUpdate", + "description": "Тело запроса на обновление документа изменения статуса", + "fields": [ + { + "name": "equipmentId", + "type": "Equipment", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": "ChangeEquipmentStatus.equipmentId", + "sync": false, + "label": null + }, + { + "name": "newStatus", + "type": "EquipmentStatus", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Новый статус", + "map": "ChangeEquipmentStatus.newStatus", + "sync": false, + "label": null + }, + { + "name": "number", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Номер", + "map": "ChangeEquipmentStatus.number", + "sync": false, + "label": null + }, + { + "name": "date", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Дата изменения статуса", + "map": "ChangeEquipmentStatus.date", + "sync": false, + "label": null + }, + { + "name": "responsible", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": "Ответственный", + "map": "ChangeEquipmentStatus.responsible", + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.ChangeEquipmentStatusListRequest", + "description": "Запрос для постраничного получения списка документов изменения статуса с фильтрацией", + "fields": [ + { + "name": "page", + "type": "DTO.PageRequest", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "filterEquipmentId", + "type": "uuid", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "filterNumber", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "filterDate", + "type": "date", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "filterResponsible", + "type": "string", + "required": false, + "nullable": true, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + } + ] + }, + { + "name": "DTO.ChangeEquipmentStatusListResponse", + "description": "Ответ с постраничным списком документов изменения статуса и метаданными", + "fields": [ + { + "name": "content", + "type": "DTO.ChangeEquipmentStatus[]", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + }, + { + "name": "pageInfo", + "type": "DTO.PageInfo", + "required": false, + "nullable": false, + "unique": false, + "primary": false, + "description": null, + "map": null, + "sync": false, + "label": null + } + ] + } + ], + "apis": [ + { + "name": "API.Equipment", + "description": "API управления справочником оборудования", + "endpoints": [ + { + "name": "listEquipment", + "label": "POST /equipment/page", + "method": "POST", + "path": "/equipment/page", + "description": "Постраничный список оборудования с фильтрацией", + "attributes": [ + { + "name": "request", + "type": "DTO.EquipmentListRequest", + "description": null + }, + { + "name": "response", + "type": "DTO.EquipmentListResponse", + "description": null + } + ] + }, + { + "name": "getEquipment", + "label": "GET /equipment/{id}", + "method": "GET", + "path": "/equipment/{id}", + "description": "Получить единицу оборудования по идентификатору", + "attributes": [ + { + "name": "id", + "type": "uuid", + "description": null + }, + { + "name": "response", + "type": "DTO.Equipment", + "description": null + } + ] + }, + { + "name": "createEquipment", + "label": "POST /equipment", + "method": "POST", + "path": "/equipment", + "description": "Создать новую единицу оборудования", + "attributes": [ + { + "name": "request", + "type": "DTO.EquipmentCreate", + "description": null + } + ] + }, + { + "name": "updateEquipment", + "label": "PUT /equipment/{id}", + "method": "PUT", + "path": "/equipment/{id}", + "description": "Обновить данные единицы оборудования", + "attributes": [ + { + "name": "id", + "type": "uuid", + "description": null + }, + { + "name": "request", + "type": "DTO.EquipmentUpdate", + "description": null + } + ] + }, + { + "name": "deleteEquipment", + "label": "DELETE /equipment/{id}", + "method": "DELETE", + "path": "/equipment/{id}", + "description": "Удалить единицу оборудования", + "attributes": [ + { + "name": "id", + "type": "uuid", + "description": null + } + ] + } + ] + }, + { + "name": "API.EquipmentStatusChange", + "description": "API управления документами изменения статуса оборудования", + "endpoints": [ + { + "name": "listStatusChanges", + "label": "POST /status-changes/page", + "method": "POST", + "path": "/status-changes/page", + "description": "Постраничный список документов изменения статуса с фильтрацией", + "attributes": [ + { + "name": "request", + "type": "DTO.ChangeEquipmentStatusListRequest", + "description": null + }, + { + "name": "response", + "type": "DTO.ChangeEquipmentStatusListResponse", + "description": null + } + ] + }, + { + "name": "getStatusChange", + "label": "GET /status-changes/{id}", + "method": "GET", + "path": "/status-changes/{id}", + "description": "Получить документ изменения статуса по идентификатору", + "attributes": [ + { + "name": "id", + "type": "uuid", + "description": null + }, + { + "name": "response", + "type": "DTO.ChangeEquipmentStatus", + "description": null + } + ] + }, + { + "name": "createStatusChange", + "label": "POST /status-changes", + "method": "POST", + "path": "/status-changes", + "description": "Создать документ изменения статуса оборудования", + "attributes": [ + { + "name": "request", + "type": "DTO.ChangeEquipmentStatusCreate", + "description": null + } + ] + }, + { + "name": "updateStatusChange", + "label": null, + "method": null, + "path": null, + "description": "Обновить документ изменения статуса", + "attributes": [ + { + "name": "id", + "type": "uuid", + "description": null + }, + { + "name": "request", + "type": "DTO.ChangeEquipmentStatusUpdate", + "description": null + } + ] + }, + { + "name": "deleteStatusChange", + "label": "DELETE /status-changes/{id}", + "method": "DELETE", + "path": "/status-changes/{id}", + "description": "Удалить документ изменения статуса", + "attributes": [ + { + "name": "id", + "type": "uuid", + "description": null + } + ] + } + ] + } + ] +} diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..f0da5a7 --- /dev/null +++ b/client/.env.example @@ -0,0 +1,4 @@ +VITE_API_URL=http://localhost:3000 +VITE_KEYCLOAK_URL=https://sso.greact.ru +VITE_KEYCLOAK_REALM=toir +VITE_KEYCLOAK_CLIENT_ID=toir-frontend diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/Dockerfile b/client/Dockerfile new file mode 100644 index 0000000..d1a0607 --- /dev/null +++ b/client/Dockerfile @@ -0,0 +1,14 @@ +FROM node:22-alpine AS build +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +FROM nginx:1.27-alpine +COPY nginx/default.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html + +EXPOSE 80 diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..7dbf7eb --- /dev/null +++ b/client/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/client/eslint.config.js b/client/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/client/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..3269aca --- /dev/null +++ b/client/index.html @@ -0,0 +1,13 @@ + + + + + + + client + + +
+ + + diff --git a/client/nginx/default.conf b/client/nginx/default.conf new file mode 100644 index 0000000..1fdae79 --- /dev/null +++ b/client/nginx/default.conf @@ -0,0 +1,20 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location /api/ { + proxy_pass http://server:3000/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..34cb8a5 --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,4384 @@ +{ + "name": "client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.9", + "@mui/material": "^7.3.9", + "keycloak-js": "^26.2.3", + "ra-data-simple-rest": "^5.14.5", + "react": "^19.2.4", + "react-admin": "^5.14.5", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.0", + "vite": "^8.0.1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "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", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "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==", + "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", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.9.tgz", + "integrity": "sha512-MOkOCTfbMJwLshlBCKJ59V2F/uaLYfmKnN76kksj6jlGUVdI25A9Hzs08m+zjBRdLv+sK7Rqdsefe8X7h/6PCw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.9.tgz", + "integrity": "sha512-BT+zPJXss8Hg/oEMRmHl17Q97bPACG4ufFSfGEdhiE96jOyR5Dz1ty7ZWt1fVGR0y1p+sSgEwQT/MNZQmoWDCw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.3.9", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.9.tgz", + "integrity": "sha512-I8yO3t4T0y7bvDiR1qhIN6iBWZOTBfVOnmLlM7K6h3dx5YX2a7rnkuXzc2UkZaqhxY9NgTnEbdPlokR1RxCNRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "@mui/core-downloads-tracker": "^7.3.9", + "@mui/system": "^7.3.9", + "@mui/types": "^7.4.12", + "@mui/utils": "^7.3.9", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.3", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.3.9", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.9.tgz", + "integrity": "sha512-ErIyRQvsiQEq7Yvcvfw9UDHngaqjMy9P3JDPnRAaKG5qhpl2C4tX/W1S4zJvpu+feihmZJStjIyvnv6KDbIrlw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "@mui/utils": "^7.3.9", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.9.tgz", + "integrity": "sha512-JqujWt5bX4okjUPGpVof/7pvgClqh7HvIbsIBIOOlCh2u3wG/Bwp4+E1bc1dXSwkrkp9WUAoNdI5HEC+5HKvMw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.9.tgz", + "integrity": "sha512-aL1q9am8XpRrSabv9qWf5RHhJICJql34wnrc1nz0MuOglPRYF/liN+c8VqZdTvUn9qg+ZjRVbKf4sJVFfIDtmg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "@mui/private-theming": "^7.3.9", + "@mui/styled-engine": "^7.3.9", + "@mui/types": "^7.4.12", + "@mui/utils": "^7.3.9", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz", + "integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.3.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.9.tgz", + "integrity": "sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "@mui/types": "^7.4.12", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "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.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "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.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tanstack/query-core": { + "version": "5.96.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.96.2.tgz", + "integrity": "sha512-hzI6cTVh4KNRk8UtoIBS7Lv9g6BnJPXvBKsvYH1aGWvv0347jT3BnSvztOE+kD76XGvZnRC/t6qdW1CaIfwCeA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.96.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.96.2.tgz", + "integrity": "sha512-sYyzzJT4G0g02azzJ8o55VFFV31XvFpdUpG+unxS0vSaYsJnSPKGoI6WdPwUucJL1wpgGfwfmntNX/Ub1uOViA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.96.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "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.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "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": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/autosuggest-highlight": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/autosuggest-highlight/-/autosuggest-highlight-3.3.4.tgz", + "integrity": "sha512-j6RETBD2xYnrVcoV1S5R4t3WxOlWZKyDQjkwnggDPSjF5L4jV98ZltBpvPvbkM1HtoSe5o+bNrTHyjPbieGeYA==", + "license": "MIT", + "dependencies": { + "remove-accents": "^0.4.2" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.14.tgz", + "integrity": "sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001785", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001785.tgz", + "integrity": "sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==", + "license": "BSD" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/diacritic": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/diacritic/-/diacritic-0.0.2.tgz", + "integrity": "sha512-iQCeDkSPwkfwWPr+HZZ49WRrM2FSI9097Q9w7agyRCdLcF9Eh2Ek0sHKcmMWx2oZVBjRBE/sziGFjZu0uf1Jbg==", + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "license": "MIT", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "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", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflection": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", + "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonexport": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonexport/-/jsonexport-3.2.0.tgz", + "integrity": "sha512-GbO9ugb0YTZatPd/hqCGR0FSwbr82H6OzG04yzdrG7XOe4QZ0jhQ+kOsB29zqkzoYJLmLxbbrFiuwbQu891XnQ==", + "license": "Apache-2.0", + "bin": { + "jsonexport": "bin/jsonexport.js" + } + }, + "node_modules/keycloak-js": { + "version": "26.2.3", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.2.3.tgz", + "integrity": "sha512-widjzw/9T6bHRgEp6H/Se3NCCarU7u5CwFKBcwtu7xfA1IfdZb+7Q7/KGusAnBo34Vtls8Oz9vzSqkQvQ7+b4Q==", + "license": "Apache-2.0", + "workspaces": [ + "test" + ] + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/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/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/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/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/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/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/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/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/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/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/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/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/minimatch": { + "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": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-polyglot": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-polyglot/-/node-polyglot-2.6.0.tgz", + "integrity": "sha512-ZZFkaYzIfGfBvSM6QhA9dM8EEaUJOVewzGSRcXWbJELXDj0lajAtKaENCYxvF5yE+TgHg6NQb0CmgYMsMdcNJQ==", + "license": "BSD-2-Clause", + "dependencies": { + "hasown": "^2.0.2", + "object.entries": "^1.1.8", + "warning": "^4.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ra-core": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/ra-core/-/ra-core-5.14.5.tgz", + "integrity": "sha512-Ztis8Rb1f0XZbnT3ZQTP0W6dmpb26S/ml/IhtSKG299p6t7Pm70DE1N5QA1G+AM3gx3iz17LX5gJSv7+iuly2A==", + "license": "MIT", + "dependencies": { + "date-fns": "^3.6.0", + "eventemitter3": "^5.0.1", + "inflection": "^3.0.0", + "jsonexport": "^3.2.0", + "lodash": "^4.17.21", + "query-string": "^7.1.3", + "react-error-boundary": "^4.0.13", + "react-is": "^18.2.0 || ^19.0.0" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.83.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", + "react-hook-form": "^7.65.0", + "react-router": "^6.28.1 || ^7.1.1", + "react-router-dom": "^6.28.1 || ^7.1.1" + } + }, + "node_modules/ra-data-simple-rest": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/ra-data-simple-rest/-/ra-data-simple-rest-5.14.5.tgz", + "integrity": "sha512-raC0R0VfYkKf+TqnEZKOcAOBZCF/8OD1ssW4foxla0p6TV9+lIm8SPauLAoaSE0gk16hevAfuvIOOwOOCCsS/w==", + "license": "MIT", + "dependencies": { + "query-string": "^7.1.3" + }, + "peerDependencies": { + "ra-core": "^5.0.0" + } + }, + "node_modules/ra-i18n-polyglot": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/ra-i18n-polyglot/-/ra-i18n-polyglot-5.14.5.tgz", + "integrity": "sha512-qP/qxAgyoJgxGJBqWZdliHc5vCK8WZM7YL3Jkre8URnmru9hPZD3vilQH4LSW/W+5yLfYoLk81597Gig9g9euw==", + "license": "MIT", + "dependencies": { + "node-polyglot": "^2.2.2", + "ra-core": "^5.14.5" + } + }, + "node_modules/ra-language-english": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/ra-language-english/-/ra-language-english-5.14.5.tgz", + "integrity": "sha512-5lB3O1/gNSgvKRDR1NA9yOEMSwGdrJYzv4etpLBaVVnAX268BHubZbm1pLmT7j4xEc9kvUQC58/rAnFDORqbiQ==", + "license": "MIT", + "dependencies": { + "ra-core": "^5.14.5" + } + }, + "node_modules/ra-ui-materialui": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/ra-ui-materialui/-/ra-ui-materialui-5.14.5.tgz", + "integrity": "sha512-0FNrBL6k+Yu1PLRZOKAc6TDGcRk8ZAWBb0OnujlvdCOgYHgFKYtLa4nZ5L9a0EYsUsOogbmx4zKiETsJUYIfqg==", + "license": "MIT", + "dependencies": { + "autosuggest-highlight": "^3.1.1", + "clsx": "^2.1.1", + "css-mediaquery": "^0.1.2", + "diacritic": "^0.0.2", + "dompurify": "^3.2.4", + "inflection": "^3.0.0", + "jsonexport": "^3.2.0", + "lodash": "~4.17.5", + "query-string": "^7.1.3", + "react-dropzone": "^14.2.3", + "react-error-boundary": "^4.0.13", + "react-hotkeys-hook": "^5.1.0", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "@mui/icons-material": "^5.16.12 || ^6.0.0 || ^7.0.0", + "@mui/material": "^5.16.12 || ^6.0.0 || ^7.0.0", + "@mui/system": "^5.15.20 || ^6.0.0 || ^7.0.0", + "@mui/utils": "^5.15.20 || ^6.0.0 || ^7.0.0", + "@tanstack/react-query": "^5.83.0", + "csstype": "^3.1.3", + "ra-core": "^5.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", + "react-hook-form": "*", + "react-is": "^18.0.0 || ^19.0.0", + "react-router": "^6.28.1 || ^7.1.1", + "react-router-dom": "^6.28.1 || ^7.1.1" + } + }, + "node_modules/ra-ui-materialui/node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-admin": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/react-admin/-/react-admin-5.14.5.tgz", + "integrity": "sha512-1XQxqL+ArkL3o5USs/AsC9fdQX4F/vzBJXwelq8jEXABcbx58d51Ug2WkXi+7WuxPrLZCOPW1kVD7l4+ahbeww==", + "license": "MIT", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^5.16.12 || ^6.0.0 || ^7.0.0", + "@mui/material": "^5.16.12 || ^6.0.0 || ^7.0.0", + "@tanstack/react-query": "^5.83.0", + "ra-core": "^5.14.5", + "ra-i18n-polyglot": "^5.14.5", + "ra-language-english": "^5.14.5", + "ra-ui-materialui": "^5.14.5", + "react-hook-form": "^7.65.0", + "react-router": "^6.28.1 || ^7.1.1", + "react-router-dom": "^6.28.1 || ^7.1.1" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-dropzone": { + "version": "14.4.1", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.4.1.tgz", + "integrity": "sha512-QDuV76v3uKbHiH34SpwifZ+gOLi1+RdsCO1kl5vxMT4wW8R82+sthjvBw4th3NHF/XX6FBsqDYZVNN+pnhaw0g==", + "license": "MIT", + "dependencies": { + "attr-accept": "^2.2.4", + "file-selector": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, + "node_modules/react-error-boundary": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.1.2.tgz", + "integrity": "sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.72.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.1.tgz", + "integrity": "sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-hotkeys-hook": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-5.2.4.tgz", + "integrity": "sha512-BgKg+A1+TawkYluh5Bo4cTmcgMN5L29uhJbDUQdHwPX+qgXRjIPYU5kIDHyxnAwCkCBiu9V5OpB2mpyeluVF2A==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-is": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "license": "MIT" + }, + "node_modules/react-router": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz", + "integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz", + "integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.14.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/remove-accents": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.4.tgz", + "integrity": "sha512-EpFcOa/ISetVHEXqu+VwI96KZBmq+a8LJnGkaeFw45epGlxIZz5dhEEnNZMsQXgORu3qaMoLX4qJCzOik6ytAg==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "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.1.0", + "esbuild": "^0.27.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/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..91ff980 --- /dev/null +++ b/client/package.json @@ -0,0 +1,37 @@ +{ + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.9", + "@mui/material": "^7.3.9", + "keycloak-js": "^26.2.3", + "ra-data-simple-rest": "^5.14.5", + "react": "^19.2.4", + "react-admin": "^5.14.5", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.0", + "vite": "^8.0.1" + } +} diff --git a/client/public/favicon.svg b/client/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/client/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/icons.svg b/client/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/client/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/App.css b/client/src/App.css new file mode 100644 index 0000000..f90339d --- /dev/null +++ b/client/src/App.css @@ -0,0 +1,184 @@ +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..f6bf538 --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,30 @@ +import { Resource } from 'react-admin'; +import EquipmentCreate from './resources/equipment/EquipmentCreate'; +import EquipmentEdit from './resources/equipment/EquipmentEdit'; +import EquipmentList from './resources/equipment/EquipmentList'; +import EquipmentShow from './resources/equipment/EquipmentShow'; +import ChangeEquipmentStatusCreate from './resources/change-equipment-status/ChangeEquipmentStatusCreate'; +import ChangeEquipmentStatusEdit from './resources/change-equipment-status/ChangeEquipmentStatusEdit'; +import ChangeEquipmentStatusList from './resources/change-equipment-status/ChangeEquipmentStatusList'; +import ChangeEquipmentStatusShow from './resources/change-equipment-status/ChangeEquipmentStatusShow'; + +export default function App() { + return ( + <> + + + + ); +} diff --git a/client/src/assets/hero.png b/client/src/assets/hero.png new file mode 100644 index 0000000..cc51a3d Binary files /dev/null and b/client/src/assets/hero.png differ diff --git a/client/src/assets/react.svg b/client/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/client/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/vite.svg b/client/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/client/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/client/src/auth/authProvider.ts b/client/src/auth/authProvider.ts new file mode 100644 index 0000000..5377136 --- /dev/null +++ b/client/src/auth/authProvider.ts @@ -0,0 +1,63 @@ +import type { AuthProvider } from 'react-admin'; +import { initKeycloak, keycloak } from './keycloak'; + +const getRealmRoles = () => { + const parsed = keycloak.tokenParsed as { realm_access?: { roles?: string[] } } | undefined; + return parsed?.realm_access?.roles ?? []; +}; + +export const authProvider: AuthProvider = { + async login() { + await initKeycloak(); + await keycloak.login(); + }, + async logout() { + await keycloak.logout({ + redirectUri: window.location.origin, + }); + }, + async checkAuth() { + await initKeycloak(); + + if (!keycloak.authenticated) { + await keycloak.login(); + } + }, + async checkError(error) { + const status = error?.status; + + if (status === 401) { + await keycloak.login(); + return Promise.reject(error); + } + + if (status === 403) { + return Promise.reject(error); + } + + return Promise.resolve(); + }, + async getIdentity() { + await initKeycloak(); + + const parsed = keycloak.tokenParsed as + | { + sub?: string; + preferred_username?: string; + name?: string; + email?: string; + } + | undefined; + + return { + id: parsed?.sub ?? 'anonymous', + fullName: parsed?.name ?? parsed?.preferred_username ?? 'User', + avatar: undefined, + email: parsed?.email, + }; + }, + async getPermissions() { + await initKeycloak(); + return getRealmRoles(); + }, +}; diff --git a/client/src/auth/keycloak.ts b/client/src/auth/keycloak.ts new file mode 100644 index 0000000..0cb03f4 --- /dev/null +++ b/client/src/auth/keycloak.ts @@ -0,0 +1,47 @@ +import Keycloak from 'keycloak-js'; +import { env } from '../config/env'; + +export const keycloak = new Keycloak({ + url: env.keycloakUrl, + realm: env.keycloakRealm, + clientId: env.keycloakClientId, +}); + +let initPromise: Promise | null = null; +let refreshPromise: Promise | null = null; + +export async function initKeycloak() { + if (!initPromise) { + initPromise = keycloak.init({ + onLoad: 'login-required', + pkceMethod: 'S256', + checkLoginIframe: false, + }); + } + + await initPromise; + return keycloak; +} + +export async function getAccessToken() { + await initKeycloak(); + + if (!keycloak.authenticated) { + return null; + } + + if (!refreshPromise) { + refreshPromise = keycloak + .updateToken(30) + .then(() => keycloak.token ?? null) + .catch(async () => { + await keycloak.login(); + return keycloak.token ?? null; + }) + .finally(() => { + refreshPromise = null; + }); + } + + return refreshPromise; +} diff --git a/client/src/bootstrap.tsx b/client/src/bootstrap.tsx new file mode 100644 index 0000000..d9445ed --- /dev/null +++ b/client/src/bootstrap.tsx @@ -0,0 +1,8 @@ +import { Admin } from 'react-admin'; +import { authProvider } from './auth/authProvider'; +import { dataProvider } from './dataProvider'; +import App from './App'; + +export function BootstrapApp() { + return ; +} diff --git a/client/src/config/env.ts b/client/src/config/env.ts new file mode 100644 index 0000000..9d0d935 --- /dev/null +++ b/client/src/config/env.ts @@ -0,0 +1,6 @@ +export const env = { + apiUrl: import.meta.env.VITE_API_URL ?? '/api', + keycloakUrl: import.meta.env.VITE_KEYCLOAK_URL ?? 'https://sso.greact.ru', + keycloakRealm: import.meta.env.VITE_KEYCLOAK_REALM ?? 'toir', + keycloakClientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID ?? 'toir-frontend', +}; diff --git a/client/src/dataProvider.ts b/client/src/dataProvider.ts new file mode 100644 index 0000000..d80583c --- /dev/null +++ b/client/src/dataProvider.ts @@ -0,0 +1,223 @@ +import type { + CreateParams, + DataProvider, + DeleteParams, + GetListParams, + GetManyParams, + GetManyReferenceParams, + GetOneParams, + RaRecord, + UpdateParams, +} from 'react-admin'; +import { fetchUtils } from 'react-admin'; +import { env } from './config/env'; +import { getAccessToken } from './auth/keycloak'; + +type ResourceConfig = { + path: string; + compositeKeys?: string[]; +}; + +const resourceConfig: Record = { + equipment: { path: 'equipment' }, + 'equipment-status-changes': { + path: 'change-equipment-status', + compositeKeys: ['equipmentId', 'newStatus'], + }, + 'change-equipment-statuses': { + path: 'change-equipment-status', + compositeKeys: ['equipmentId', 'newStatus'], + }, +}; + +const getResourceConfig = (resource: string): ResourceConfig => { + return resourceConfig[resource] ?? { path: resource }; +}; + +const normalizeRecord = (resource: string, record: Record) => { + const config = getResourceConfig(resource); + if (!config.compositeKeys || record.id) { + return record; + } + + const parts = config.compositeKeys.map((key) => String(record[key] ?? '')); + return { + ...record, + id: parts.join(':'), + }; +}; + +const buildCompositePath = (resource: string, params: { id?: string | number; data?: Partial }) => { + const config = getResourceConfig(resource); + if (!config.compositeKeys) { + return String(params.id); + } + + const source = params.id != null ? {} : (params.data ?? {}); + const values = config.compositeKeys.map((key) => { + if (source[key] != null) { + return String(source[key]); + } + + const id = String(params.id ?? ''); + const parts = id.split(':'); + const index = config.compositeKeys?.indexOf(key) ?? -1; + return parts[index] ?? ''; + }); + + return values.map((value) => encodeURIComponent(value)).join('/'); +}; + +const buildQueryString = (params: Record) => { + const searchParams = new URLSearchParams(); + + for (const [key, value] of Object.entries(params)) { + if (value == null || value === '') { + continue; + } + + if (Array.isArray(value)) { + value.forEach((entry) => { + if (entry != null && entry !== '') { + searchParams.append(key, String(entry)); + } + }); + continue; + } + + searchParams.append(key, String(value)); + } + + const encoded = searchParams.toString(); + return encoded ? `?${encoded}` : ''; +}; + +const httpClient = async (url: string, options: RequestInit = {}) => { + const token = await getAccessToken(); + const headers = new Headers(options.headers ?? {}); + + if (!headers.has('Accept')) { + headers.set('Accept', 'application/json'); + } + + if (!headers.has('Content-Type') && options.body) { + headers.set('Content-Type', 'application/json'); + } + + if (token) { + // headers.set('Authorization', `Bearer ${token}`); + headers.set('Authorization', `Bearer ${token}`); + } + + return fetchUtils.fetchJson(url, { + ...options, + headers, + }); +}; + +const parseListResponse = async (resource: string, url: string, options?: RequestInit) => { + const response = await httpClient(url, options); + const totalHeader = response.headers.get('Content-Range') ?? 'items 0-0/0'; + const total = Number(totalHeader.split('/').at(-1) ?? 0); + const data = Array.isArray(response.json) + ? response.json.map((item) => normalizeRecord(resource, item)) + : []; + + return { + data, + total, + }; +}; + +const getOne: DataProvider['getOne'] = async (resource: string, params: GetOneParams) => { + const { path } = getResourceConfig(resource); + const id = buildCompositePath(resource, params); + const response = await httpClient(`${env.apiUrl}/${path}/${id}`); + + return { + data: normalizeRecord(resource, response.json), + }; +}; + +const getList: DataProvider['getList'] = async (resource: string, params: GetListParams) => { + const { path } = getResourceConfig(resource); + const query = buildQueryString({ + _start: params.pagination?.page && params.pagination?.perPage + ? (params.pagination.page - 1) * params.pagination.perPage + : 0, + _end: params.pagination?.page && params.pagination?.perPage + ? params.pagination.page * params.pagination.perPage + : 25, + _sort: params.sort?.field, + _order: params.sort?.order, + q: typeof params.filter?.q === 'string' ? params.filter.q : undefined, + ...params.filter, + }); + + return parseListResponse(resource, `${env.apiUrl}/${path}${query}`); +}; + +export const dataProvider: DataProvider = { + async getList(resource: string, params: GetListParams) { + return getList(resource, params); + }, + async getOne(resource: string, params: GetOneParams) { + return getOne(resource, params); + }, + async getMany(resource: string, params: GetManyParams) { + const results = await Promise.all(params.ids.map((id) => getOne(resource, { id }))); + + return { + data: results.map((result) => result.data), + }; + }, + async getManyReference(resource: string, params: GetManyReferenceParams) { + return getList(resource, { + ...params, + filter: { + ...params.filter, + [params.target]: params.id, + }, + }); + }, + async update(resource: string, params: UpdateParams) { + const { path } = getResourceConfig(resource); + const id = buildCompositePath(resource, params); + const response = await httpClient(`${env.apiUrl}/${path}/${id}`, { + method: 'PATCH', + body: JSON.stringify(params.data), + }); + + return { + data: normalizeRecord(resource, response.json), + }; + }, + async updateMany() { + throw new Error('updateMany is not implemented'); + }, + async create(resource: string, params: CreateParams) { + const { path } = getResourceConfig(resource); + const response = await httpClient(`${env.apiUrl}/${path}`, { + method: 'POST', + body: JSON.stringify(params.data), + }); + + return { + data: normalizeRecord(resource, response.json), + }; + }, + async delete(resource: string, params: DeleteParams) { + const { path } = getResourceConfig(resource); + const id = buildCompositePath(resource, params); + const response = await httpClient(`${env.apiUrl}/${path}/${id}`, { + method: 'DELETE', + }); + + return { + data: normalizeRecord(resource, response.json ?? params.previousData ?? { id: params.id }), + }; + }, + async deleteMany() { + throw new Error('deleteMany is not implemented'); + }, +}; diff --git a/client/src/index.css b/client/src/index.css new file mode 100644 index 0000000..ae50ef5 --- /dev/null +++ b/client/src/index.css @@ -0,0 +1,26 @@ +:root { + font-family: Inter, system-ui, sans-serif; + line-height: 1.5; + font-weight: 400; + color: #162033; + background: #f4f7fb; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +html, +body, +#root { + margin: 0; + min-height: 100%; +} + +body { + min-height: 100vh; +} diff --git a/client/src/main.tsx b/client/src/main.tsx new file mode 100644 index 0000000..04bc716 --- /dev/null +++ b/client/src/main.tsx @@ -0,0 +1,22 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import { initKeycloak } from './auth/keycloak'; +import './index.css'; +import App from './App'; + +async function bootstrap() { + await initKeycloak(); + + const container = document.getElementById('root'); + if (!container) { + throw new Error('Root container not found'); + } + + createRoot(container).render( + + + , + ); +} + +void bootstrap(); diff --git a/client/src/resources/change-equipment-status/ChangeEquipmentStatusCreate.tsx b/client/src/resources/change-equipment-status/ChangeEquipmentStatusCreate.tsx new file mode 100644 index 0000000..e9339ab --- /dev/null +++ b/client/src/resources/change-equipment-status/ChangeEquipmentStatusCreate.tsx @@ -0,0 +1,32 @@ +import { + AutocompleteInput, + Create, + DateInput, + ReferenceInput, + SelectInput, + SimpleForm, + TextInput as RaTextInput, +} from 'react-admin'; +import { equipmentOptionText, equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +export default function ChangeEquipmentStatusCreate() { + return ( + + + + ({ q: searchText })} + /> + + + {/* TextInput source="number" */} + + {/* date-fields-separator ..................................................................................................................................................................................................................... */} + + {/* TextInput source="responsible" */} + + + + ); +} diff --git a/client/src/resources/change-equipment-status/ChangeEquipmentStatusEdit.tsx b/client/src/resources/change-equipment-status/ChangeEquipmentStatusEdit.tsx new file mode 100644 index 0000000..0be403f --- /dev/null +++ b/client/src/resources/change-equipment-status/ChangeEquipmentStatusEdit.tsx @@ -0,0 +1,32 @@ +import { + AutocompleteInput, + DateInput, + Edit, + ReferenceInput, + SelectInput, + SimpleForm, + TextInput as RaTextInput, +} from 'react-admin'; +import { equipmentOptionText, equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +export default function ChangeEquipmentStatusEdit() { + return ( + + + + ({ q: searchText })} + /> + + + {/* TextInput source="number" */} + + {/* date-fields-separator ..................................................................................................................................................................................................................... */} + + {/* TextInput source="responsible" */} + + + + ); +} diff --git a/client/src/resources/change-equipment-status/ChangeEquipmentStatusList.tsx b/client/src/resources/change-equipment-status/ChangeEquipmentStatusList.tsx new file mode 100644 index 0000000..6646e4c --- /dev/null +++ b/client/src/resources/change-equipment-status/ChangeEquipmentStatusList.tsx @@ -0,0 +1,43 @@ +import { + Datagrid, + DateField, + FilterButton, + List, + ReferenceField, + SelectArrayInput, + SelectField, + TextField, + TextInput, + TopToolbar, +} from 'react-admin'; +import { equipmentOptionText, equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +const statusChangeFilters = [ + , + , + , + , +]; + +const StatusChangeListActions = () => ( + + + +); + +export default function ChangeEquipmentStatusList() { + return ( + } filters={statusChangeFilters}> + + + + + + + + + + + + ); +} diff --git a/client/src/resources/change-equipment-status/ChangeEquipmentStatusShow.tsx b/client/src/resources/change-equipment-status/ChangeEquipmentStatusShow.tsx new file mode 100644 index 0000000..ce66dfc --- /dev/null +++ b/client/src/resources/change-equipment-status/ChangeEquipmentStatusShow.tsx @@ -0,0 +1,26 @@ +import { + DateField, + ReferenceField, + SelectField, + Show, + SimpleShowLayout, + TextField, +} from 'react-admin'; +import { equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +export default function ChangeEquipmentStatusShow() { + return ( + + + + + + + + + + + + + ); +} diff --git a/client/src/resources/equipment-status-change/EquipmentStatusChangeCreate.tsx b/client/src/resources/equipment-status-change/EquipmentStatusChangeCreate.tsx new file mode 100644 index 0000000..bb777a5 --- /dev/null +++ b/client/src/resources/equipment-status-change/EquipmentStatusChangeCreate.tsx @@ -0,0 +1 @@ +export { default } from '../change-equipment-status/ChangeEquipmentStatusCreate'; diff --git a/client/src/resources/equipment-status-change/EquipmentStatusChangeEdit.tsx b/client/src/resources/equipment-status-change/EquipmentStatusChangeEdit.tsx new file mode 100644 index 0000000..978aca9 --- /dev/null +++ b/client/src/resources/equipment-status-change/EquipmentStatusChangeEdit.tsx @@ -0,0 +1 @@ +export { default } from '../change-equipment-status/ChangeEquipmentStatusEdit'; diff --git a/client/src/resources/equipment-status-change/EquipmentStatusChangeList.tsx b/client/src/resources/equipment-status-change/EquipmentStatusChangeList.tsx new file mode 100644 index 0000000..6707c43 --- /dev/null +++ b/client/src/resources/equipment-status-change/EquipmentStatusChangeList.tsx @@ -0,0 +1 @@ +export { default } from '../change-equipment-status/ChangeEquipmentStatusList'; diff --git a/client/src/resources/equipment-status-change/EquipmentStatusChangeShow.tsx b/client/src/resources/equipment-status-change/EquipmentStatusChangeShow.tsx new file mode 100644 index 0000000..566714f --- /dev/null +++ b/client/src/resources/equipment-status-change/EquipmentStatusChangeShow.tsx @@ -0,0 +1 @@ +export { default } from '../change-equipment-status/ChangeEquipmentStatusShow'; diff --git a/client/src/resources/equipment/EquipmentCreate.tsx b/client/src/resources/equipment/EquipmentCreate.tsx new file mode 100644 index 0000000..159558f --- /dev/null +++ b/client/src/resources/equipment/EquipmentCreate.tsx @@ -0,0 +1,19 @@ +import { Create, DateInput, SelectInput, SimpleForm, TextInput as RaTextInput } from 'react-admin'; +import { equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +export default function EquipmentCreate() { + return ( + + + {/* TextInput source="name" */} + + {/* TextInput source="serialNumber" */} + + {/* date-fields-separator ..................................................................................................................................................................................................................... */} + + + + + + ); +} diff --git a/client/src/resources/equipment/EquipmentEdit.tsx b/client/src/resources/equipment/EquipmentEdit.tsx new file mode 100644 index 0000000..17ddf19 --- /dev/null +++ b/client/src/resources/equipment/EquipmentEdit.tsx @@ -0,0 +1,19 @@ +import { DateInput, Edit, SelectInput, SimpleForm, TextInput as RaTextInput } from 'react-admin'; +import { equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +export default function EquipmentEdit() { + return ( + + + {/* TextInput source="name" */} + + {/* TextInput source="serialNumber" */} + + {/* date-fields-separator ..................................................................................................................................................................................................................... */} + + + + + + ); +} diff --git a/client/src/resources/equipment/EquipmentList.tsx b/client/src/resources/equipment/EquipmentList.tsx new file mode 100644 index 0000000..3399643 --- /dev/null +++ b/client/src/resources/equipment/EquipmentList.tsx @@ -0,0 +1,41 @@ +import { + Datagrid, + DateField, + FilterButton, + List, + NumberField, + SelectArrayInput, + SelectField, + TextField, + TextInput, + TopToolbar, +} from 'react-admin'; +import { equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +const equipmentFilters = [ + , + , + , +]; + +const EquipmentListActions = () => ( + + + +); + +export default function EquipmentList() { + return ( + } filters={equipmentFilters}> + + + + + + + + + + + ); +} diff --git a/client/src/resources/equipment/EquipmentShow.tsx b/client/src/resources/equipment/EquipmentShow.tsx new file mode 100644 index 0000000..d08a3bc --- /dev/null +++ b/client/src/resources/equipment/EquipmentShow.tsx @@ -0,0 +1,17 @@ +import { DateField, SelectField, Show, SimpleShowLayout, TextField } from 'react-admin'; +import { equipmentStatusChoices } from '../shared/equipmentStatusChoices'; + +export default function EquipmentShow() { + return ( + + + + + + + + + + + ); +} diff --git a/client/src/resources/shared/equipmentStatusChoices.ts b/client/src/resources/shared/equipmentStatusChoices.ts new file mode 100644 index 0000000..535f502 --- /dev/null +++ b/client/src/resources/shared/equipmentStatusChoices.ts @@ -0,0 +1,34 @@ +export const equipmentStatusChoices = [ + { id: 'Active', name: 'Active' }, + { id: 'Repair', name: 'Repair' }, +]; + +export const equipmentOptionText = (record?: { + inventoryNumber?: string; + code?: string; + number?: string; + name?: string; + id?: string; +}) => { + if (!record) { + return ''; + } + + if (record.inventoryNumber) { + return `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}`; + } + + if (record.code) { + return `${record.code} — ${record.name ?? record.code}`; + } + + if (record.number) { + return `${record.number} — ${record.name ?? record.number}`; + } + + if (record.name) { + return record.name ?? String(record.id ?? ''); + } + + return String(record.id ?? ''); +}; diff --git a/client/src/vite-env.d.ts b/client/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/client/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/client/tsconfig.app.json b/client/tsconfig.app.json new file mode 100644 index 0000000..af516fc --- /dev/null +++ b/client/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2023", + "useDefineForClassFields": true, + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json new file mode 100644 index 0000000..8a67f62 --- /dev/null +++ b/client/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/client/vite.config.ts b/client/vite.config.ts new file mode 100644 index 0000000..8b0f57b --- /dev/null +++ b/client/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/db-seed/Dockerfile b/db-seed/Dockerfile new file mode 100644 index 0000000..c759920 --- /dev/null +++ b/db-seed/Dockerfile @@ -0,0 +1,9 @@ +FROM postgres:16 +WORKDIR /seed + +COPY equipment-import.sql /seed/equipment-import.sql +COPY db-seed/import.sh /seed/import.sh + +RUN chmod +x /seed/import.sh + +CMD ["/seed/import.sh"] diff --git a/db-seed/import.sh b/db-seed/import.sh new file mode 100644 index 0000000..52f0841 --- /dev/null +++ b/db-seed/import.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -eu + +export PGPASSWORD="${POSTGRES_PASSWORD:-postgres}" + +until pg_isready -h "${POSTGRES_HOST:-postgres}" -U "${POSTGRES_USER:-postgres}" -d "${POSTGRES_DB:-toir}"; do + sleep 2 +done + +if [ -f /seed/equipment-import.sql ]; then + psql \ + -h "${POSTGRES_HOST:-postgres}" \ + -U "${POSTGRES_USER:-postgres}" \ + -d "${POSTGRES_DB:-toir}" \ + -f /seed/equipment-import.sql +fi diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..67c796f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +services: + postgres: + image: postgres:16 + environment: + POSTGRES_DB: toir + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - "${POSTGRES_PORT:-5432}:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + + server: + build: + context: ./server + env_file: + - ./server/.env.example + environment: + DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/toir" + depends_on: + - postgres + ports: + - "${SERVER_PORT:-3000}:3000" + + db-seed: + build: + context: . + dockerfile: db-seed/Dockerfile + environment: + POSTGRES_HOST: postgres + POSTGRES_DB: toir + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + depends_on: + - postgres + + client: + build: + context: ./client + depends_on: + - server + ports: + - "${CLIENT_PORT:-8080}:80" + +volumes: + postgres-data: diff --git a/docs/AID_EXPORT_README.md b/docs/AID_EXPORT_README.md new file mode 100644 index 0000000..288f625 --- /dev/null +++ b/docs/AID_EXPORT_README.md @@ -0,0 +1,120 @@ +# AID: экспорт OpenAPI и генератор приложения + +В репозитории добавлены **сервисы-экспортёры** для интеграции с **AID** (или любым другим клиентом по HTTP): автоматическое получение **OpenAPI 3.0** из доменного **api-format**. + +--- + +## Что работает + +| Компонент | Назначение | +|-----------|------------| +| **`POST /aid/export/openapi`** (NestJS) | На вход JSON **api-format** → на выход документ **OpenAPI 3.0** в поле `openapi`. | +| **`tools/api-format-to-openapi/`** | CLI и промпт для LLM: тот же конвертер, что вызывает Nest. | +| **`server/src/aid-export/`** | Модуль Nest: контроллер, сервис, краткая справка в `README.md` рядом с кодом. | + +## Что временно не работает + +| Компонент | Статус | +|-----------|--------| +| **`POST /aid/export/app`** (DSL → бандл/apply) | **Non-operative.** The backing script (`generation/generate.mjs`) was removed during the architecture migration to api.dsl-first LLM generation. The endpoint returns 500 with a descriptive error. | + +--- + +## Требования к запуску + +1. Репозиторий клонирован целиком (есть `tools/`, `server/`, `client/`). +2. Backend запускается из каталога **`server/`** (`npm run start` / `start:dev`), чтобы относительные пути `../tools/api-format-to-openapi/convert.mjs` были корректны. +3. Для режима OpenAPI через LLM на сервере нужны **`OPENAI_API_KEY`** (и при необходимости `OPENAI_MODEL`, `OPENAI_BASE_URL`). + +--- + +## Переменные окружения (`server/.env`) + +| Переменная | Зачем | +|------------|--------| +| `AID_EXPORT_API_KEY` | Если задана, к **`/aid/export/*`** нужен заголовок **`X-AID-Export-Key`** с тем же значением. | +| `OPENAI_API_KEY` | Для `POST /aid/export/openapi` с **`"mode": "llm"`**. | + +Остальное как для обычного бэкенда (`DATABASE_URL`, `PORT` и т.д.). + +--- + +## HTTP API (интеграция с AID) + +Базовый URL: `http://:` (например `http://localhost:3000`). + +### 1. OpenAPI из api-format + +**`POST /aid/export/openapi`** + +```http +Content-Type: application/json +X-AID-Export-Key: <если задан AID_EXPORT_API_KEY> +``` + +```json +{ + "apiFormat": { + "apiFormatVersion": "1", + "info": { "title": "API", "version": "1.0.0" }, + "server": { "basePath": "/api" }, + "resources": [] + }, + "mode": "deterministic" +} +``` + +- **`mode`**: `deterministic` (по умолчанию) — маппинг в коде для схемы версии `1`; **`llm`** — вызов OpenAI по промпту из `tools/api-format-to-openapi/prompts/llm-system.md`. + +**Ответ:** `{ "openapi": { "openapi": "3.0.3", ... } }` + +Пример входа для теста: `tools/api-format-to-openapi/examples/api-format.example.json` (подставьте как значение `apiFormat`). + +### 2. Генератор приложения из DSL (non-operative) + +**`POST /aid/export/app`** + +> **Non-operative.** This endpoint depended on `generation/generate.mjs` which was removed +> during the migration to api.dsl-first LLM generation. It currently returns HTTP 500 +> with a descriptive error message. Restoring this endpoint requires implementing a new +> backing script compatible with the current `domain/*.api.dsl` pipeline. + +--- + +## CLI (без Nest) + +### api-format → OpenAPI + +```bash +cd tools/api-format-to-openapi +node convert.mjs --in examples/api-format.example.json --out ../../openapi.generated.json +``` + +LLM: + +```bash +set OPENAI_API_KEY=sk-... +node convert.mjs --mode llm --in your-api-format.json --out ../../openapi.llm.json +``` + +Подробнее: **`tools/api-format-to-openapi/README.md`**. + +--- + +## Типичный сценарий для AID + +1. AID уже сформировал **api-format** (как у вас принято после DTO). +2. AID вызывает **`POST /aid/export/openapi`** → получает **OpenAPI 3.0** → сохраняет в проект / отдаёт в Swagger / в реестр. + +--- + +## Где смотреть код и короткую справку + +- Полное описание эндпоинтов рядом с реализацией: **`server/src/aid-export/README.md`** + +--- + +## Ограничения и дальнейшие шаги + +- Пример **api-format** в репозитории — **учебный**; под ваш продакшен-формат может понадобиться расширить маппинг в `convert.mjs` или отточить промпт **`llm-system.md`**. +- **`/aid/export/app`** requires a new backing implementation compatible with the `domain/*.api.dsl` pipeline to be restored. See `docs/future-work.md` for planned future work. diff --git a/docs/api-dsl-conventions.md b/docs/api-dsl-conventions.md new file mode 100644 index 0000000..4b29e3e --- /dev/null +++ b/docs/api-dsl-conventions.md @@ -0,0 +1,162 @@ +# api.dsl Conventions + +Grammar and authoring conventions for `domain/*.api.dsl` files. +See `domain/toir.api.dsl` as the canonical example. + +--- + +## File location and naming + +- All api.dsl files live in `domain/`. +- Naming: `.api.dsl` (e.g. `toir.api.dsl`). +- Multiple api.dsl files are allowed but not required. + +--- + +## Two top-level block types + +An api.dsl file contains two types of top-level blocks: + +1. `dto` — defines the shape of a data transfer object +2. `api` — declares a group of API endpoints for one resource + +--- + +## dto block + +``` +dto DTO. { + description "Human-readable description"; + + attribute { + description "Field description"; + type ; + is required; // or: is nullable; + map .; // links to a field in domain/*.api.dsl + } +} +``` + +### DTO naming convention + +| DTO name | Purpose | +|----------|---------| +| `DTO.` | Full response shape (GET by id, list items) | +| `DTO.Create` | Create request body | +| `DTO.Update` | Update request body (partial — all fields nullable) | +| `DTO.ListRequest` | Paginated list request (filters + page) | +| `DTO.ListResponse` | Paginated list response (content + page info) | + +### Types + +Same scalar types as the domain DSL: +`uuid`, `string`, `text`, `integer`, `decimal`, `date`, `boolean`, or an enum name from +`domain/*.api.dsl`. + +Cross-DTO references: `DTO.` or `DTO.[]`. + +Standard pagination types (not entity-specific; treated as well-known): +`DTO.Filter[]`, `DTO.PageRequest`, `DTO.PageInfo`. + +### is required vs is nullable + +Unlike the domain DSL (where these drive DB constraints), in api.dsl these drive the +TypeScript DTO property modifier: + +- `is required` → `field!: type` in the generated class +- `is nullable` → `field?: type` in the generated class +- Neither modifier → treated as optional (`field?: type`) + +### map directive + +`map Entity.field` links the DTO attribute to a domain entity field. + +Rules: +- The entity and field must exist in `domain/*.api.dsl`. +- The scalar type must match the domain DSL field type (nullability may differ). +- Omit `map` only for pagination helper types (`DTO.Filter[]`, `DTO.PageRequest`, etc.) + that have no direct entity field counterpart. + +### Conflict resolution + +If the type declared in a `dto` attribute conflicts with the domain DSL field type, +the domain DSL is correct. Fix the api.dsl. + +--- + +## api block + +``` +api API. { + description "API group description"; + + endpoint { + label "METHOD /path"; + description "Endpoint description"; + + // For endpoints with a request body: + attribute request { + type DTO.; + } + + // For endpoints with a response body: + attribute response { + type DTO.; + } + + // For path parameters: + attribute { + type ; + } + } +} +``` + +### api block naming + +`API.` (e.g. `API.Equipment`, `API.RepairOrder`) + +### endpoint label + +`label "METHOD /path"` is the authoritative declaration of HTTP verb and route. + +| Label pattern | NestJS decorator | Notes | +|---------------|-----------------|-------| +| `"GET /resource/{id}"` | `@Get(':id')` | | +| `"POST /resource"` | `@Post()` | Create | +| `"POST /resource/page"` | `@Post('page')` | Paginated list (body-based filter) | +| `"PUT /resource/{id}"` | `@Put(':id')` | Full or partial update | +| `"DELETE /resource/{id}"` | `@Delete(':id')` | | + +Do not infer HTTP verbs from endpoint names. Always read the `label`. + +### Path parameters + +Declared as a plain `attribute` inside the endpoint block (not wrapped in `request` +or `response`): + +``` +attribute id { + type uuid; +} +``` + +### Standard 5-endpoint CRUD pattern + +| Endpoint name | Label | Body | +|---------------|-------|------| +| `lists` | `"POST //page"` | request: `DTO.ListRequest`, response: `DTO.ListResponse` | +| `get` | `"GET //{id}"` | path param `id`, response: `DTO.` | +| `create` | `"POST /"` | request: `DTO.Create` | +| `update` | `"PUT //{id}"` | path param `id`, request: `DTO.Update` | +| `delete` | `"DELETE //{id}"` | path param `id` | + +--- + +## Constraints + +1. Every `map Entity.field` must resolve to an existing entity + field in `domain/*.api.dsl`. +2. Types in api.dsl must be compatible with domain DSL field types (same scalar type). +3. api.dsl must not define entities or enums. Those belong in `domain/*.api.dsl`. +4. An api.dsl may omit domain entity fields from a DTO (e.g. no PK in Create DTO). + It must not add fields that don't exist in the domain model. diff --git a/docs/completion-contract.md b/docs/completion-contract.md new file mode 100644 index 0000000..a9f42a0 --- /dev/null +++ b/docs/completion-contract.md @@ -0,0 +1,121 @@ +# Completion Contract + +This document defines the definition of done for a KIS-TOiR generation run. It provides prioritized success criteria, failure modes, and recovery procedures to ensure consistent, production-ready outcomes. + +These criteria apply to the post-generation repository state. In a repo-wide full regeneration driven by `prompts/general-prompt.md`, the run intentionally starts without relying on pre-existing generated workspaces or runtime artifacts. + +## Prioritized Success Criteria + +### Tier 1: Infrastructure Readiness (Must Pass) + +- By the end of the run, `server/` exists and builds successfully (`npm run build`) +- By the end of the run, `client/` exists and builds successfully (`npm run build`) +- `node tools/validate-generation.mjs --artifacts-only` passes (stable infra/runtime/deploy checks) +- Required scaffold files present (NestJS/Vite essentials) +- Runtime/deploy baseline files are present and coherent: `docker-compose.yml`, `server/Dockerfile`, `client/Dockerfile`, `client/nginx/default.conf`, env templates, and the realm artifact +- scaffolded package manifests are normalized to the repository-approved exact version policy instead of floating on `latest` or caret ranges + +### Tier 2: Domain Fidelity (Must Pass) + +- `npm run eval:generation` passes (DSL fidelity, CRUD behavior, UX invariants) +- DTOs match DSL shapes exactly +- Controllers have correct guards and roles +- React Admin resources use type-correct components +- Natural-key handling correct +- Prisma schema reflects DSL without manual edits +- Reviewed eval contracts lead any new or changed entity behavior before regeneration; automation may scaffold candidate contracts, but it must not silently rewrite the committed eval corpus during every run +- Specialized generator outputs respect frozen-contract write-zones and do not leak unauthorized cross-layer edits + +### Tier 3: Runtime/Deploy Readiness (Must Pass) + +- Production Dockerfiles exist and are valid +- Reverse-proxy nginx config supports SPA routing and `/api` proxying +- Compose topology matches the repository contract: `postgres`, `server`, `db-seed`, and `client`, with Keycloak external to the compose stack +- Env examples align with auth/runtime contracts +- Realm artifact matches frontend/backend expectations +- Runtime/deploy artifacts are treated as first-class generation targets, not optional extras + +### Tier 3A: Prisma Migration Policy (Current Explicit State) + +- Current state: accepted temporary technical debt +- Immediate contract: `server/prisma/schema.prisma` must match the DSL +- Current runtime behavior: if committed migrations exist, runtime uses `prisma migrate deploy`; if not, bootstrap flows may fall back to `prisma db push` +- Preferred target: commit reviewed Prisma migrations for each DSL-driven schema change and remove the `db push` fallback from production/runtime paths + +### Tier 4: Auth Seam Integrity (Must Pass) + +- JWKS resolution works (no silent failures) +- Role extraction from `realm_access.roles` +- `/health` remains public +- 401/403 semantics correct + +## Failure Modes and Recovery + +### Failure Mode: Scaffold Degradation + +**Symptoms**: Build fails, missing Nest/Vite files. +**Recovery**: Run official CLI repair (`nest new` or `vite create`), then regenerate domain code. + +### Failure Mode: DSL Mismatch + +**Symptoms**: Eval fails on DTO shapes or field mappings. +**Recovery**: Re-read DSL entity block, regenerate affected artifacts, re-run evals. + +### Failure Mode: Delegated Output Drift + +**Symptoms**: A specialized generator edits unauthorized zones, violates the frozen contract, or hands back output that is not integration-ready. +**Recovery**: Allow one bounded repair pass. If drift remains, reject the output explicitly and only then use parent manual fallback. + +### Failure Mode: Guard/Header Issues + +**Symptoms**: Eval fails on guards or Content-Range. +**Recovery**: Fix guard order (JwtAuthGuard first), ensure Content-Range format (`items`), re-run evals. + +### Failure Mode: Relation Annotation Missing + +**Symptoms**: Prisma schema invalid. +**Recovery**: Add explicit `@relation(fields: [...], references: [...])`, regenerate schema. + +### Failure Mode: Runtime/Deploy Gaps + +**Symptoms**: Docker build fails, nginx proxy broken. +**Recovery**: Update `prompts/runtime-rules.md`, regenerate the affected runtime/deploy artifacts, and verify the deploy baseline again. + +### Failure Mode: Auth Seam Drift + +**Symptoms**: JWKS fails, roles not extracted. +**Recovery**: Verify JWKS order, ensure `realm_access.roles`, regenerate auth artifacts. + +## Recovery Procedures + +1. **Partial Regenerate**: If only one entity fails, regenerate that entity only. +2. **Full Reset**: If scaffold broken or a repo-wide regeneration is requested, remove the generated Tier 3 app/runtime outputs (`server/`, `client/`, `db-seed/`, root runtime artifacts), re-scaffold from official CLIs, and regenerate all. +3. **Rollback**: If generation introduces breaking changes, revert to last passing commit. +4. **Debug Mode**: Run `node tools/validate-generation.mjs` with `--run-runtime` for deeper checks. + +## Acceptance Protocol + +Parent acceptance is explicit for all delegated generation outputs. + +A delegated output is accepted only if: + +- only allowed zones were modified +- the frozen contract is respected +- no unauthorized cross-layer edits occurred +- the result is integration-ready +- relevant checks were attempted where applicable +- unresolved issues are surfaced explicitly + +Escalation rule: + +- allow at most one bounded repair pass +- reject explicitly if the output still is not usable +- manual fallback is allowed only after rejection + +## Definition of Complete + +A generation run is complete when all Tier 1-4 criteria pass, the explicit +Prisma migration policy state is acknowledged, contract freeze and delegated +acceptance rules were respected, the reviewer signs off, the package manifests +follow the approved version policy, and the app is runnable/deployable with the +repository-managed runtime/deploy artifacts and documented external dependencies. diff --git a/docs/future-work.md b/docs/future-work.md new file mode 100644 index 0000000..5821e21 --- /dev/null +++ b/docs/future-work.md @@ -0,0 +1,180 @@ +# Future Work — Deferred Items + +This file tracks engineering improvements that are deliberately deferred due to the +current stage of the project. They are not forgotten — they are acknowledged technical +debt that should be addressed before scaling. + +--- + +## Rule 7 — Tracing, Telemetry, Cost/Latency Observability + +**Status:** Deferred. No LLM calls are instrumented. + +**Why it matters (Anthropic / Google / Microsoft guidance):** +Without observability, you cannot: +- Know which prompts are expensive (token count, latency) +- Detect prompt regressions via cost drift +- Attribute generation failures to specific prompt versions +- Track improvement over time + +**What needs to be built:** + +### 7.1 — Generation log + +Create `tools/generation-log.mjs` that wraps any LLM generation call and writes a +structured JSON entry to `logs/generation.jsonl`: + +```json +{ + "timestamp": "2026-04-03T10:00:00.000Z", + "entity": "Equipment", + "artifact": "backend", + "prompt_version": "1.0", + "model": "...", + "input_tokens": 4200, + "output_tokens": 1800, + "latency_ms": 3200, + "validation_passed": true, + "eval_passed": true +} +``` + +### 7.2 — Cost budget alerts + +Add a threshold check (e.g., warn if input_tokens > 8000 for a single entity generation). +This enforces the context budget from `prompts/general-prompt.md §CONTEXT BUDGET`. + +### 7.3 — Prompt version tracking + +Add `` comments to all prompt files (already started in +`backend-rules.md` and `frontend-rules.md`). Increment version on any non-trivial change. +Log the prompt versions alongside the generation log entry. + +### 7.4 — Drift detection + +Compare generation log entries across runs. If token count for the same entity increases +by >20% without a DSL change, flag it as context rot. + +**Effort estimate:** Medium. 2–3 days to build the logging layer. Zero effort for +prompt versioning (already partially done). + +**Trigger:** Implement before the system is used for more than 10 entities or before +any production deployment. + +--- + +## Rule 8 — Risk Controls and Red-Teaming + +**Status:** Deferred. No sanitization or adversarial testing exists. + +**Why it matters (Anthropic / Google / Microsoft guidance):** +LLM-generated code at scale introduces risks that do not exist in hand-written code: +- **Prompt injection**: malicious content in DSL `description` fields could steer + generation (e.g., `description "Ignore previous instructions and..."`) +- **Generated credential leakage**: LLM may hallucinate hardcoded secrets that look + real (e.g., `apiKey: 'sk-...'`) +- **Missing auth guards**: already caught by Rule 4 validator, but adversarial prompts + could bypass it by generating valid-looking guard syntax that is semantically inactive +- **Supply chain**: generated package imports could reference non-existent or malicious + packages if the LLM hallucinates + +**What needs to be built:** + +### 8.1 — DSL input sanitization + +In `tools/api-summary.mjs`, before building the summary, check all `description` and +`label` fields for injection patterns: + +```javascript +function sanitizeDslString(value, fieldPath) { + const injectionPatterns = [ + /ignore previous/i, + /disregard.*instruction/i, + /you are now/i, + /system:/i, + ]; + for (const pattern of injectionPatterns) { + if (pattern.test(value)) { + throw new Error(`Potential prompt injection in DSL field ${fieldPath}: "${value}"`); + } + } + return value; +} +``` + +### 8.2 — Generated code security scan + +Add to `tools/validate-generation.mjs` (or a separate `tools/security-scan.mjs`): + +```javascript +// Check no hardcoded secrets leaked into generated code +function validateNoSecretLeakage() { + const patterns = [ + /sk-[a-zA-Z0-9]{20,}/, // OpenAI key pattern + /[a-zA-Z0-9+/]{40}={0,2}/, // Base64 secret-like + /password\s*=\s*['"][^'"]{4,}['"]/, // Hardcoded password + /apiKey\s*=\s*['"][^'"]{4,}['"]/, // Hardcoded API key + ]; + // Run against all generated files... +} +``` + +### 8.3 — UseGuards completeness audit + +Beyond the current semantic gate coverage, add: verify that the guard +constructor arguments are non-empty and match the expected guard class names. A guard +call like `@UseGuards()` (empty) should fail eval coverage because it provides no protection. + +### 8.4 — Red-team fixture + +Create `tools/eval/fixtures/_adversarial/` with a fixture that includes a DSL snippet +containing a benign injection attempt (e.g., a `description` field with "ignore format +rules") and verifies the generation still produces spec-compliant output. + +### 8.5 — Generated import allowlist + +Maintain a list of approved npm packages that generated code may import. Flag any +import not on the allowlist as a manual review item. + +**Effort estimate:** Medium-High. 3–5 days. Security scan and sanitization are low +effort; red-team fixtures and import allowlisting are higher effort. + +**Trigger:** Implement before any external user can influence `domain/*.api.dsl` content +(i.e., before a UI or API to edit the DSL is exposed). + +--- + +## Tracking + +| Rule | Status | Priority | Trigger | +|------|--------|----------|---------| +| Rule 7 — Telemetry | Deferred | Medium | Before >10 entities or production deployment | +| Rule 8 — Risk controls | Deferred | High | Before DSL editing is exposed to external users | +| Rule 9 — Eval corpus automation | Deferred | Medium | After the contract stabilizes for the current entity set | + +--- + +## Rule 9 — Eval Corpus Automation + +**Status:** Deferred. Reviewed eval fixtures are still the authoritative semantic gate. + +**Why it matters:** +The repository already requires eval-first behavior for new or changed entity coverage, but the repo does not yet synthesize starter eval contracts from source-of-truth. That means humans still have to review and finalize the first failing semantic contract. + +**What needs to be built:** + +- a deterministic helper that can scaffold `tools/eval/fixtures//` starters from the active source-of-truth slice +- a prompt-to-eval helper that emits backend and frontend assertion starters before regeneration +- a documented workflow where generated starters are reviewed and committed, instead of silently replacing the authoritative eval corpus on every run + +**Status of current contract:** + +- This is not implemented yet. +- The repo should not imply that evals are auto-generated today. +- The repo should also not imply that a step-0 LLM subagent should rewrite committed eval fixtures on every regeneration run; that would collapse the independence of the semantic gate. +- Until bounded automation exists, the reviewed eval-first rule in `prompts/general-prompt.md` and `docs/generation-playbook.md` remains required. + +**Effort estimate:** Medium. +**Trigger:** After the contract stabilizes for the current entity set. + +Last updated: 2026-04-03 diff --git a/docs/generation-playbook.md b/docs/generation-playbook.md new file mode 100644 index 0000000..018326f --- /dev/null +++ b/docs/generation-playbook.md @@ -0,0 +1,333 @@ +# Generation Playbook + +Step-by-step instructions for generating and regenerating artifacts in this repository. +Read `AGENTS.md` and `docs/source-of-truth.md` first. + +--- + +## Pipeline overview + +``` +Tier 1 — Single Source of Truth (hand-authored, never generated) + domain/toir.api.dsl ──┐ enums, DTOs, endpoints, HTTP methods, + │ entity field mappings, primary keys + │ + ▼ +Tier 2 — Deterministic Preprocessing (npm scripts, no LLM) + api-summary.json ←─ npm run generate:api-summary + openapi.json ←─ npm run generate:openapi (auxiliary) + │ + ▼ +Tier 1 (api.dsl) + prompts/*.md ──► LLM Generation + prompts/general-prompt.md + parent orchestrator ──► discovery, docs verification, contract freeze, shared scaffold, auth/runtime skeleton, integration + prompts/prisma-rules.md ──► generator_prisma ──► server/prisma/schema.prisma + prompts/backend-rules.md ──► generator_nest_resources ──► server/src/modules// + prompts/frontend-rules.md ──► generator_react_admin_resources ──► client/src/resources// + prompts/auth-rules.md ──► parent + generator_data_access ──► server/src/auth/, client/src/auth/, client/src/dataProvider.ts, toir-realm.json + prompts/runtime-rules.md ──► parent ──► docker-compose.yml, server/client Dockerfiles, nginx config, env templates, docker-entrypoint, db-seed artifacts + prompts/validation-rules.md ──► (validation gate reference) + │ + ▼ +Tier 4 — Validation Gate + node tools/validate-generation.mjs --artifacts-only + npm run eval:generation +``` + +--- + +## Prerequisites + +Before any generation run: + +1. `domain/*.api.dsl` is current and valid. +2. Read `AGENTS.md` and `prompts/general-prompt.md`. +3. If this is a repo-wide full regeneration, begin from Tier 1/Tier 2 inputs without relying on existing Tier 3 outputs such as `server/`, `client/`, `db-seed/`, `docker-compose.yml`, or `toir-realm.json`. +4. Refresh the Tier 2 intermediate context only when validator/tooling or a supporting workflow needs it: + ```bash + npm run generate:api-summary + ``` + +Do not require `node tools/validate-generation.mjs --artifacts-only` to pass before a clean-slate full regeneration. That command validates the post-generation repository state. + +--- + +## Standard generation workflow + +### Step 0 — Full-regeneration reset when requested + +For a repo-wide full regeneration driven by `prompts/general-prompt.md`: + +- do not assume `server/`, `client/`, `db-seed/`, Dockerfiles, env templates, `docker-compose.yml`, or `toir-realm.json` already exist +- recreate backend and frontend workspaces from the official NestJS and Vite React TypeScript CLIs +- regenerate Tier 3 runtime/deploy artifacts after scaffold recreation +- treat the validator and eval harness as end-state checks after generation + +### Step 1 — Refresh auxiliary derived artifacts only when needed + +```bash +# From repo root +npm run generate:api-summary +``` + +### Step 2 — Read generation inputs (context budget) + +> **`prompts/general-prompt.md` is the master generation prompt.** It contains all core +> type mappings, naming conventions, DTO/controller/service/frontend rules, mutation +> boundaries, and the complete generation workflow. Load it as the single entrypoint. +> +> For artifact-specific details (Prisma FK rules, auth JWKS chain, detailed validation +> groups), load the relevant companion file: `prompts/prisma-rules.md`, +> `prompts/auth-rules.md`, `prompts/validation-rules.md`, or `prompts/runtime-rules.md`. +> +> See `prompts/general-prompt.md §CONTEXT BUDGET` for the full budget model. + +1. `prompts/general-prompt.md` — master generation prompt (always load) +2. `domain/*.api.dsl §API.` — **only the api block + its referenced DTOs + enums** (entity-scoped) +3. `api-summary.json` — only when validator/tooling explicitly needs the auxiliary freshness artifact +4. **If needed:** `prompts/prisma-rules.md` (Prisma), `prompts/auth-rules.md` (auth seams), `prompts/runtime-rules.md` (deploy/runtime artifacts), or `prompts/validation-rules.md` (gate ownership) + +Before generating any DTO or component: **quote the relevant DSL field definitions verbatim first**, then generate from those quotes. This prevents training-data contamination. +If a new or changed entity behavior is not already covered by an eval contract, write or review the failing eval first so the semantic gate leads the generation change. Helpers may scaffold candidate fixtures from source-of-truth, but committed evals remain reviewed artifacts rather than auto-regenerated outputs. + +### Step 3 — Discovery and docs verification + +- Use `explorer` first for repo discovery, scaffold inspection, and tracing shared seams. +- Use `docs_researcher` before planning framework-sensitive auth, runtime, Prisma, NestJS, or React Admin work. +- Prefer Context7 for official framework/library docs; use web fallback only for current or missing details. + +### Step 4 — Freeze the contract + +Before specialized generation, the parent must freeze a normalized structured handoff that captures: + +- entities, fields, types, ids/composite keys, relations, and enums +- endpoint, route, and naming conventions +- auth surface expectations +- validator/eval compatibility constraints +- allowed write-zones for each delegated generator + +This freeze is a parent-owned protocol. It does not replace the DSL as source of truth. + +### Step 5 — Shared scaffold and parent-owned seams + +- repair or recreate official Nest/Vite/Prisma scaffolds as needed +- parent owns auth platform skeleton, deploy/runtime skeleton, env conventions, and shared wiring +- do not start specialized generators before scaffold and frozen-contract inputs are ready + +### Step 6 — Generate Prisma schema + +Generate `server/prisma/schema.prisma` from `domain/*.api.dsl` following `prompts/prisma-rules.md`. + +Preferred migration policy: + +- if the DSL changes the schema, create or update Prisma migrations in `server/` +- keep `db push` only as a bootstrap fallback for fresh environments that do not yet have migration history +- treat schema changes shipped without a migration as temporary technical debt that must be called out explicitly + +If the schema changed, follow the explicit repository migration policy: + +```bash +cd server +npx prisma migrate dev --name +``` + +If committed migrations do not yet exist, the current repository accepts `prisma db push` only as temporary bootstrap debt. Do not present that fallback as the target steady state. + +Use `generator_prisma` for this bounded scope. It must not modify backend modules, frontend resources, auth, or runtime/deploy artifacts. + +### Step 7 — Generate backend modules + +For each `api` block in `domain/*.api.dsl`, generate: + +1. `server/src/modules//.module.ts` +2. `server/src/modules//dto/create-.dto.ts` + — fields from the `DTO.Create` block in api.dsl +3. `server/src/modules//dto/update-.dto.ts` + — fields from the `DTO.Update` block in api.dsl +4. `server/src/modules//.service.ts` + — CRUD operations using Prisma; respect type mappings from `prompts/backend-rules.md` +5. `server/src/modules//.controller.ts` + — one method per `endpoint` in the `api` block; HTTP verb and path from the `label` + +Register the module in `server/src/app.module.ts`. + +Use `generator_nest_resources` for this bounded scope. It must stay within backend resource zones and must not redesign the shared auth or runtime platform. + +### Step 8 — Generate frontend resources + +For each `api` block in `domain/*.api.dsl`, generate: + +1. `client/src/resources//List.tsx` + — columns from `DTO.` (response shape) +2. `client/src/resources//Create.tsx` + — fields from `DTO.Create` +3. `client/src/resources//Edit.tsx` + — fields from `DTO.Update` +4. `client/src/resources//Show.tsx` + — fields from `DTO.` + +Register the resource in `client/src/App.tsx`. + +Use `generator_react_admin_resources` for this bounded scope. It must stay within frontend resource zones and must not redesign shared data-access or auth strategy. + +### Step 9 — Generate data-access and auth/runtime/deploy artifacts + +Use `generator_data_access` for `client/src/dataProvider.ts` and only the narrowly delegated frontend integration seam. Parent retains ownership of the shared auth platform skeleton and all deploy/runtime skeleton artifacts. + +Generate or repair: + +1. `server/src/auth/` +2. `client/src/auth/` +3. `client/src/dataProvider.ts` +4. `toir-realm.json` +5. `docker-compose.yml` +6. `server/.env.example`, `client/.env.example` +7. `server/Dockerfile`, `client/Dockerfile` +8. `client/nginx/default.conf` +9. `server/docker-entrypoint.sh` +10. `db-seed/Dockerfile`, `db-seed/import.sh` + +Preserve the proven-good runtime behaviors from `prompts/runtime-rules.md` unless the repository contract explicitly changes them. + +### Step 10 — Accept delegated outputs and integrate + +Parent acceptance is explicit. Accept a delegated output only if: + +- only allowed zones changed +- the frozen contract is respected +- no unauthorized cross-layer edits occurred +- the result is integration-ready +- relevant checks were attempted where applicable +- unresolved issues are surfaced explicitly + +Allow at most one bounded repair pass. If still unusable, reject explicitly. +Manual fallback is allowed only after rejection. + +### Step 11 — Verify (two-stage gate) + +**Stage 1 — Structural gate:** + +```bash +node tools/validate-generation.mjs --artifacts-only +``` + +Checks scaffold/build readiness, auth/runtime/realm seams, required runtime/deploy artifacts, and other stable repository invariants. + +Full structural verification (requires installed deps): + +```bash +node tools/validate-generation.mjs +``` + +**Stage 2 — Eval harness:** + +```bash +npm run eval:generation +``` + +Fixture-based semantic checks. See `tools/eval/README.md`. + +Both must pass before the task is complete. + +### Step 12 — Final review + +Run `reviewer` only after integration and validation. Reviewer is the final +correctness/security/test-gap gate, not a substitute for parent validation. + +--- + +## Adding a new entity + +1. Add the entity's enums, DTOs, and `api` block to `domain/toir.api.dsl`. +2. Run `npm run generate:api-summary`. +3. **Before generating:** create `tools/eval/fixtures//meta.json`, `backend.assertions.json`, and `frontend.assertions.json` with expected patterns. +4. Run `npm run eval:generation` — it will fail (entity files don't exist yet). That's expected. +5. Generate backend, frontend, and runtime-facing artifacts (Steps 3–6). +6. Run `npm run eval:generation` again — now it should pass. +7. Run `node tools/validate-generation.mjs --artifacts-only` — both gates must pass. + +> This is **eval-driven development**: write the failing eval first (step 3), then generate to make it pass (step 6). A passing eval confirms the LLM followed the rules. + +--- + +## Changing an existing entity + +**If the domain model changes** (new field, changed type, new FK, new enum): + +1. Update `domain/toir.api.dsl` (enums, DTO attributes, `map` references). +2. Run `npm run generate:api-summary`. +3. Regenerate `server/prisma/schema.prisma`, run migration. +4. Regenerate the affected modules/resources and any affected runtime artifacts (Steps 4–7). + +**If only the API contract changes** (different nullability, new endpoint, different HTTP method): + +1. Update `domain/toir.api.dsl` only. +2. Run `npm run generate:api-summary` to refresh `api-summary.json`. +3. Run Steps 4–7. No Prisma migration needed. + +--- + +## Artifact traceability matrix + +| Artifact | DSL source | Prompt rule | Validator check | +| ---------------------------------------------- | ---------------------- | ---------------------------------------------- | --------------------------------------- | +| `server/prisma/schema.prisma` | `domain/*.api.dsl` | `prompts/prisma-rules.md` | `§validateBuildChecks` (file exists) | +| `api-summary.json` | `domain/*.api.dsl` | — (deterministic) | `§validateBuildChecks` (freshness) | +| `server/src/modules//*.ts` | `domain/*.api.dsl` | `prompts/backend-rules.md` | `npm run eval:generation` | +| `server/src/modules//dto/create-.dto.ts` | `DTO.Create` fields | `prompts/backend-rules.md §DTO-field-coverage` | `npm run eval:generation` | +| `server/src/modules//dto/update-.dto.ts` | `DTO.Update` fields | `prompts/backend-rules.md §DTO-field-coverage` | `npm run eval:generation` | +| `client/src/resources//*.tsx` | `domain/*.api.dsl` | `prompts/frontend-rules.md` | `npm run eval:generation` | +| `client/src/auth/keycloak.ts` | auth/runtime contract | `prompts/auth-rules.md` | `§validateAuthChecks` | +| `toir-realm.json` | auth/runtime contract | `prompts/auth-rules.md` | `§validateRealmChecks` | +| `docker-compose.yml` | runtime contract | `prompts/runtime-rules.md` | `§validateRuntimeContractChecks` | +| `server/Dockerfile` | runtime contract | `prompts/runtime-rules.md` | `§validateRuntimeContractChecks` | +| `client/Dockerfile` | runtime contract | `prompts/runtime-rules.md` | `§validateRuntimeContractChecks` | +| `client/nginx/default.conf` | runtime contract | `prompts/runtime-rules.md` | `§validateRuntimeContractChecks` | +| `server/docker-entrypoint.sh` | runtime contract | `prompts/runtime-rules.md` | `§validateRuntimeContractChecks` | +| `db-seed/Dockerfile`, `db-seed/import.sh` | runtime contract | `prompts/runtime-rules.md` | `§validateRuntimeContractChecks` | + +--- + +## Verification commands reference + +| Command | When to use | +| ----------------------------------------------------- | ------------------------------------------ | +| `npm run generate:api-summary` | After any change to `domain/*.api.dsl` | +| `npm run generate:openapi` | To regenerate OpenAPI 3.0.3 documentation | +| `node tools/validate-generation.mjs --artifacts-only` | After every generation run (required) | +| `node tools/validate-generation.mjs` | Before committing; requires installed deps | +| `node tools/validate-generation.mjs --run-runtime` | End-to-end; requires running DB | + +--- + +## OpenRouter invocation + +Set environment variables before any LLM-mode tool call: + +``` +OPENAI_API_KEY= +OPENAI_BASE_URL=https://openrouter.ai/api/v1 +OPENAI_MODEL= +``` + +The `OPENAI_BASE_URL` variable is consumed by `tools/api-format-to-openapi/convert.mjs --mode llm`. +For agent-driven generation via Cursor or direct API calls, the standard workflow above +applies regardless of model provider. + +--- + +## Auxiliary tools + +### `tools/api-summary-to-openapi.mjs` + +Generates `openapi.json` (OpenAPI 3.0.3) from `api-summary.json` deterministically. +This is a documentation/integration artifact, not part of the core generation pipeline. + +```bash +npm run generate:openapi +``` + +### `tools/api-format-to-openapi/` + +Auxiliary integration tool for external consumers using the `api-format` JSON schema. +Not connected to the main DSL pipeline. See `docs/AID_EXPORT_README.md` for details. diff --git a/docs/repository-structure.md b/docs/repository-structure.md new file mode 100644 index 0000000..d6e0962 --- /dev/null +++ b/docs/repository-structure.md @@ -0,0 +1,39 @@ +# Repository Structure + +`KIS-TOiR` keeps the existing LLM-first generation philosophy and organizes the repository by meaning: + +- `domain/` + - canonical DSL inputs + - DSL specification +- `prompts/` + - active prompt corpus used to drive generation +- `docs/` + - overview and repository-level architecture notes +- `tools/` + - helper scripts for summary generation and validation +- `server/` + - generated backend target output after generation; may be absent at the clean-slate start of a full regeneration run +- `client/` + - generated frontend target output after generation; may be absent at the clean-slate start of a full regeneration run +- `db-seed/` + - generated runtime/bootstrap output after generation; may be absent before a full regeneration run + +The repository keeps LLM-first generation orchestration, but framework bootstrap is CLI-first: + +- `server/` must remain a valid NestJS workspace baseline +- `client/` must remain a valid Vite React TypeScript workspace baseline +- repair a broken workspace before applying more domain-derived generation changes +- future agents must treat forbidden generation patterns in `prompts/` as contract violations, not suggestions + +Root-level files stay limited to repository-level artifacts such as: + +- `README.md` +- `package.json` +- `docker-compose.yml` +- `api-summary.json` +- `toir-realm.json` +- `.gitignore` + +Generated root runtime artifacts such as `docker-compose.yml` and `toir-realm.json` are end-state outputs, not clean-slate prerequisites for `prompts/general-prompt.md`. + +The repository does not introduce a new generator engine or compiler platform. It keeps the current LLM-first pipeline and makes it cleaner, more explicit, and easier to navigate. diff --git a/docs/source-of-truth.md b/docs/source-of-truth.md new file mode 100644 index 0000000..953a5ba --- /dev/null +++ b/docs/source-of-truth.md @@ -0,0 +1,144 @@ +# Source-of-Truth Hierarchy + +This document is the authoritative reference for which files own which decisions. + +--- + +## Tier 1: Authoritative sources (hand-authored; never generated) + +### `domain/*.api.dsl` + +**Single source of truth for the entire domain and API contract:** + +- Entity names and structure +- Attribute names, scalar types, descriptions +- Primary keys (including natural string keys) +- Foreign keys and relations +- Enum definitions and their values +- Database-level constraints: `is required`, `is unique`, `default` +- DTO shapes per operation (Create, Update, Read, ListRequest, ListResponse) +- Which fields appear in each DTO and with what TypeScript modifier (`!` or `?`) +- HTTP method and path for each endpoint (via `label "METHOD /path"`) +- Endpoint names (camelCase identifiers) +- Pagination request/response contract + +**Drives:** `server/prisma/schema.prisma`, `server/src/modules/`, `client/src/resources/`, +`server/src/app.module.ts`, `client/src/App.tsx`, and the generated auth/runtime/deploy +artifacts named by the companion prompt contracts. + +### `prompts/*.md` and `AGENTS.md` + +**Authoritative for:** + +- Agent generation workflow and reading order +- Parent-versus-specialist orchestration ownership +- Approved stack versions and dependency pinning policy +- Auth seam patterns (Keycloak, JWT, PKCE S256, JWKS resolution chain) +- Runtime conventions (env examples, docker-compose topology) +- Framework scaffold baseline requirements (NestJS CLI, Vite React TypeScript) +- Filtering and sorting contract +- Naming conventions and implicit rules (pluralization, sort field priority, type mappings) +- Mutation boundaries (what agents must not overwrite) +- Runtime/deploy artifact classification and preservation rules + +### `docs/completion-contract.md` + +**Operational definition of done for generation runs:** + +- prioritized success tiers +- failure modes and recovery procedures +- reviewer signoff requirement +- runtime/deploy readiness requirements + +--- + +## Tier 2: Deterministic derivatives (never edit manually) + +| File | Generated from | Command | +| ----------------- | ------------------ | ------------------------------ | +| `api-summary.json` | `domain/*.api.dsl` | `npm run generate:api-summary` | + +These files are regenerated from their sources. Manual edits are overwritten on the +next generation run. + +--- + +## Tier 3: LLM-generated artifacts (never edit manually after generation) + +These are end-state outputs. A repo-wide full regeneration may begin with some or all Tier 3 artifacts absent and then recreate them from Tier 1 inputs plus official CLI scaffolding. + +| Zone | Generated from | +| -------------------------------- | ------------------------------------------------ | +| `server/prisma/schema.prisma` | `domain/*.api.dsl` + `prompts/prisma-rules.md` | +| `server/src/modules//` | `domain/*.api.dsl` + `prompts/backend-rules.md` | +| `client/src/resources//` | `domain/*.api.dsl` + `prompts/frontend-rules.md` | +| `server/src/app.module.ts` | Module list derived from api.dsl `api` blocks | +| `client/src/App.tsx` | Resource list derived from api.dsl `api` blocks | +| `docker-compose.yml` | `prompts/runtime-rules.md` | +| `server/Dockerfile` | `prompts/runtime-rules.md` | +| `client/Dockerfile` | `prompts/runtime-rules.md` | +| `client/nginx/default.conf` | `prompts/runtime-rules.md` | +| `server/.env.example` | `prompts/runtime-rules.md` | +| `client/.env.example` | `prompts/runtime-rules.md` | +| `server/docker-entrypoint.sh` | `prompts/runtime-rules.md` | +| `db-seed/Dockerfile` | `prompts/runtime-rules.md` | +| `db-seed/import.sh` | `prompts/runtime-rules.md` | +| `server/src/auth/` | `prompts/auth-rules.md` | +| `client/src/auth/` | `prompts/auth-rules.md` | +| `client/src/dataProvider.ts` | `prompts/auth-rules.md` | +| `toir-realm.json` | `prompts/auth-rules.md` | + +To change DSL-driven domain artifacts: update `domain/*.api.dsl` and regenerate. +To change auth/runtime/deploy artifacts: update the governing prompt contracts and regenerate or repair them through the orchestrator. + +Ownership rule for Tier 3: + +- parent owns shared scaffold, auth platform skeleton, deploy/runtime skeleton, env conventions, integration, and validation +- `generator_prisma` owns `server/prisma/schema.prisma` +- `generator_nest_resources` owns `server/src/modules/**` plus delegated backend registration touchpoints +- `generator_react_admin_resources` owns `client/src/resources/**` plus delegated frontend registration touchpoints +- `generator_data_access` owns `client/src/dataProvider.ts` and any parent-delegated narrow frontend integration seam +- specialized generators must not redesign shared auth/runtime/platform seams outside their delegated zones + +Contract freeze rule: + +- before specialized generation, the parent must freeze a structured handoff covering entities, fields, types, ids/composite keys, relations, enums, endpoint/path conventions, naming rules, auth surface expectations, validator/eval constraints, and allowed write-zones per generator +- this handoff is an orchestration protocol, not a replacement for the DSL + +--- + +## Tier 4: Framework-managed support + +- Prisma migrations under `server/prisma/migrations/` when created by the official Prisma CLI +- Framework config: `nest-cli.json`, `tsconfig*.json`, `vite.config.*`, etc. +- All `prompts/*.md`, `AGENTS.md`, `domain/dsl-spec.md`, `domain/*.api.dsl` + +Runtime/deploy policy: + +- Tier 3 runtime/deploy outputs are first-class generation targets and should be regenerated or repaired from the companion rules when they drift. +- Tier 4 support files are framework-managed rather than hand-authored sources of truth. + +--- + +## Validation gate + +### Stage 1 — Structural gate + +``` +node tools/validate-generation.mjs --artifacts-only +``` + +Verifies scaffold/build readiness, required runtime/deploy artifacts, auth/runtime seams, +realm structure, and other stable repository invariants. +This gate validates the post-generation state, not the clean-slate starting state for a full regeneration run. + +### Stage 2 — Eval harness + +``` +npm run eval:generation +``` + +Fixture-based semantic checks from `tools/eval/fixtures/` for DSL fidelity, CRUD behavior, +natural-key handling, and UI invariants. + +Both stages must pass before any generation task is considered complete. diff --git a/domain/dsl-spec.md b/domain/dsl-spec.md new file mode 100644 index 0000000..f5fdb2d --- /dev/null +++ b/domain/dsl-spec.md @@ -0,0 +1,248 @@ +# DSL Language Specification + +This document describes the DSL system used to specify fullstack CRUD applications. + +`domain/*.api.dsl` is the single source of truth for the entire domain model and API +contract. It drives Prisma schema generation, NestJS module generation, and React Admin +resource generation. + +`api-summary.json` is a derived artifact generated from the api.dsl to stabilize +LLM-first generation and feed the lightweight validation gate. It must never replace the +DSL as the source of truth. The active prompt corpus that consumes this contract lives in +`prompts/`. + +--- + +# DSL Architecture + +## `domain/*.api.dsl` + +The api.dsl is the authoritative source of truth for: + +- entities and their attributes +- scalar types and enums +- primary keys and foreign keys +- database-level constraints (required, unique, default) +- relations between entities +- DTO shapes per operation (Create, Update, Read, List) +- nullability and requiredness of each DTO attribute per operation +- HTTP methods and paths for each endpoint +- endpoint names and groupings +- pagination request/response contracts + +The api.dsl drives: Prisma schema, NestJS controller/service/DTO generation, +and React Admin resource generation. + +Constraint: every `map Entity.field` or `sync Entity.field` reference in `domain/*.api.dsl` +must resolve to an entity and field defined within the same api.dsl file. + +## Optional extension mechanism + +```text +overrides/ + api-overrides.dsl + ui-overrides.dsl +``` + +Override rules: + +- Overrides are optional. +- The generator must work without them. +- Overrides may refine derived API or UI behavior, but they must not duplicate or redefine +entities, attributes, primary keys, foreign keys, relations, or enums. + +--- + +# DSL Grammar Concepts + +## entity + +An **entity** is a domain object that becomes a database table and a first-class resource in the backend and frontend. + +``` +entity Equipment { + attribute id { type uuid; key primary; } + attribute name { type string; is required; } +} +``` + +- **Domain:** Defines the canonical model; one entity = one Prisma model, one NestJS module, one React Admin resource. +- **Naming:** PascalCase (e.g. `Equipment`, `EquipmentType`, `RepairOrder`). + +--- + +## attribute + +An **attribute** is a field of an entity. It has a type and optional modifiers. + +``` +attribute name { + description "Наименование"; + type string; + is required; + is unique; +} +``` + +**Modifiers:** + +- `type` — required; one of: `string`, `uuid`, `integer`, `decimal`, `date`, `text`, `boolean`, `number`, or an enum name. +- `key primary` — this attribute is the primary key. +- `key foreign { relates Entity.field }` — foreign key to another entity's field. +- `is required` — non-nullable. +- `is unique` — unique constraint. +- `is nullable` — explicitly nullable. +- `default Value` — default value (for enums or literals). +- `description "..."` — human-readable description. +- `label "..."` — display label for UI. + +--- + +## enum + +An **enum** defines a fixed set of values. Used for attributes that can only take one of these values. + +``` +enum EquipmentStatus { + value Active { label "В эксплуатации"; } + value Repair { label "В ремонте"; } +} +``` + +- **value** — identifier used in data and code. +- **label** — optional display label for UI. + +--- + +## primary key + +Exactly one attribute per entity must be marked as primary key. + +``` +attribute id { + type uuid; + key primary; +} +``` + +Or for a natural key: + +``` +attribute code { + type string; + key primary; + is required; + is unique; +} +``` + +--- + +## foreign key + +A **foreign key** links to another entity's primary key. The attribute type must match the referenced primary key type. + +``` +attribute equipmentTypeCode { + type string; + key foreign { + relates EquipmentType.code; + } + is required; +} +``` + +- `relates Entity.attribute` — references `Entity`'s `attribute` (must be primary key). +- FK type must equal referenced PK type (e.g. `string` → `EquipmentType.code`, `uuid` → `Equipment.id`). + +--- + +## required + +- **is required** — attribute is non-nullable in the domain model and drives requiredness in derived DTO/API/UI artifacts. +- Absence of `is required` means the attribute is optional (nullable). + +--- + +## default + +- **default Value** — applied when no value is provided (e.g. enum defaults like `default Active`). +- Value must exist in the enum when the attribute type is an enum. + +--- + +# DSL → System Component Mapping + +## DSL → Prisma + + +| DSL Concept | Prisma Result | +| ------------ | --------------------------- | +| entity | model | +| attribute | field | +| enum | enum | +| key primary | @id or @id @default(uuid()) | +| key foreign | relation + references | +| type string | String | +| type uuid | String @id @default(uuid()) | +| type integer | Int | +| type number | Float | +| type decimal | Decimal | +| type date | DateTime | +| type text | String | +| type boolean | Boolean | + + +--- + +## DSL → NestJS + + +| DSL Concept | NestJS Result | +| --------------------------- | ------------------------------------- | +| entity | One module (e.g. equipment.module.ts) | +| entity | Controller with CRUD endpoints | +| entity | Service with Prisma CRUD | +| entity + attribute metadata | create-{entity}.dto.ts | +| entity + attribute metadata | update-{entity}.dto.ts | +| entity + attribute metadata | Response DTO / API shape | + + +API paths are derived from entity name: PascalCase → kebab-case, pluralized (e.g. `Equipment` → `/equipment`, `RepairOrder` → `/repair-orders`). + +--- + +## DSL → React Admin + + +| DSL Concept | React Admin Result | +| -------------------- | ----------------------------------- | +| entity | Resource (name = kebab-case plural) | +| attribute | Form field / column | +| type string | TextInput, TextField | +| type integer/decimal | NumberInput, NumberField | +| type number | NumberInput, NumberField | +| type date | DateInput, DateField | +| type boolean | BooleanInput, BooleanField | +| enum | SelectInput with choices | +| foreign key | ReferenceInput, ReferenceField | + + +--- + +# API DSL Layer Mapping + +DTO shapes and endpoint contracts are declared in `domain/*.api.dsl`. The api.dsl +is the authoritative source for: + +- **Create DTO** — declared as `dto DTO.Create` in api.dsl. Must not include +server-generated primary keys (e.g. no `id` for uuid PKs). Required/nullable per field +is explicit in the api.dsl, not inferred. +- **Update DTO** — declared as `dto DTO.Update` in api.dsl. All fields are +typically nullable for partial update semantics. +- **API response shape** — declared as `dto DTO.` in api.dsl. Must expose +React Admin-compatible `id` field. +- **UI field mapping** — derived from the DTO shapes in api.dsl, not from domain +attributes directly. The Create form uses `DTO.Create` fields; the Edit form +uses `DTO.Update` fields; List and Show use `DTO.` fields. + diff --git a/domain/toir.api.dsl b/domain/toir.api.dsl new file mode 100644 index 0000000..6272a2a --- /dev/null +++ b/domain/toir.api.dsl @@ -0,0 +1,367 @@ +dto DTO.Equipment { + description "Полный response-объект для единицы оборудования"; + attribute id { + type uuid; + map Equipment.id; + } + attribute name { + type string; + description "Название оборудования"; + map Equipment.name; + } + attribute serialNumber { + type string; + description "Заводской (серийный) номер"; + map Equipment.serialNumber; + } + attribute dateOfInspection { + type date; + is nullable; + description "Дата поверки"; + map Equipment.dateOfInspection; + } + attribute commissionedAt { + type date; + is nullable; + description "Дата изготовления"; + map Equipment.commissionedAt; + } + attribute status { + type EquipmentStatus; + description "Текущий статус"; + map Equipment.status; + } +} + +dto DTO.EquipmentCreate { + description "Тело запроса на создание единицы оборудования"; + attribute name { + type string; + description "Название оборудования"; + is required; + map Equipment.name; + } + attribute serialNumber { + type string; + description "Заводской (серийный) номер"; + is required; + map Equipment.serialNumber; + } + attribute dateOfInspection { + type date; + is nullable; + description "Дата поверки"; + map Equipment.dateOfInspection; + } + attribute commissionedAt { + type date; + is nullable; + description "Дата изготовления"; + map Equipment.commissionedAt; + } + attribute status { + type EquipmentStatus; + description "Текущий статус"; + is required; + map Equipment.status; + } +} + +dto DTO.EquipmentUpdate { + description "Тело запроса на обновление единицы оборудования"; + attribute name { + type string; + description "Название оборудования"; + is nullable; + map Equipment.name; + } + attribute serialNumber { + type string; + description "Заводской (серийный) номер"; + is nullable; + map Equipment.serialNumber; + } + attribute dateOfInspection { + type date; + is nullable; + description "Дата поверки"; + map Equipment.dateOfInspection; + } + attribute commissionedAt { + type date; + is nullable; + description "Дата изготовления"; + map Equipment.commissionedAt; + } + attribute status { + type EquipmentStatus; + description "Текущий статус"; + is nullable; + map Equipment.status; + } +} + +dto DTO.EquipmentListRequest { + description "Запрос для постраничного получения списка оборудования с фильтрацией"; + attribute page { + type DTO.PageRequest; + } + attribute filterName { + type string; + is nullable; + } + attribute filterSerialNumber { + type string; + is nullable; + } + attribute filterStatus { + type EquipmentStatus; + is nullable; + } +} + +dto DTO.EquipmentListResponse { + description "Ответ с постраничным списком оборудования и метаданными"; + attribute content { + type DTO.Equipment[]; + } + attribute pageInfo { + type DTO.PageInfo; + } +} + +dto DTO.ChangeEquipmentStatus { + description "Полный response-объект для документа изменения статуса"; + attribute equipmentId { + type Equipment; + is nullable; + map ChangeEquipmentStatus.equipmentId; + } + attribute newStatus { + type EquipmentStatus; + description "Новый статус"; + map ChangeEquipmentStatus.newStatus; + } + attribute number { + type string; + description "Номер"; + is nullable; + map ChangeEquipmentStatus.number; + } + attribute date { + type date; + description "Дата изменения статуса"; + map ChangeEquipmentStatus.date; + } + attribute responsible { + type string; + description "Ответственный"; + is nullable; + map ChangeEquipmentStatus.responsible; + } +} + +dto DTO.ChangeEquipmentStatusCreate { + description "Тело запроса на создание документа изменения статуса"; + attribute equipmentId { + type Equipment; + is nullable; + map ChangeEquipmentStatus.equipmentId; + } + attribute newStatus { + type EquipmentStatus; + description "Новый статус"; + is required; + map ChangeEquipmentStatus.newStatus; + } + attribute number { + type string; + description "Номер"; + is nullable; + map ChangeEquipmentStatus.number; + } + attribute date { + type date; + description "Дата изменения статуса"; + is required; + map ChangeEquipmentStatus.date; + } + attribute responsible { + type string; + description "Ответственный"; + is nullable; + map ChangeEquipmentStatus.responsible; + } +} + +dto DTO.ChangeEquipmentStatusUpdate { + description "Тело запроса на обновление документа изменения статуса"; + attribute equipmentId { + type Equipment; + is nullable; + map ChangeEquipmentStatus.equipmentId; + } + attribute newStatus { + type EquipmentStatus; + description "Новый статус"; + is nullable; + map ChangeEquipmentStatus.newStatus; + } + attribute number { + type string; + description "Номер"; + is nullable; + map ChangeEquipmentStatus.number; + } + attribute date { + type date; + description "Дата изменения статуса"; + is nullable; + map ChangeEquipmentStatus.date; + } + attribute responsible { + type string; + description "Ответственный"; + is nullable; + map ChangeEquipmentStatus.responsible; + } +} + +dto DTO.ChangeEquipmentStatusListRequest { + description "Запрос для постраничного получения списка документов изменения статуса с фильтрацией"; + attribute page { + type DTO.PageRequest; + } + attribute filterEquipmentId { + type uuid; + is nullable; + } + attribute filterNumber { + type string; + is nullable; + } + attribute filterDate { + type date; + is nullable; + } + attribute filterResponsible { + type string; + is nullable; + } +} + +dto DTO.ChangeEquipmentStatusListResponse { + description "Ответ с постраничным списком документов изменения статуса и метаданными"; + attribute content { + type DTO.ChangeEquipmentStatus[]; + } + attribute pageInfo { + type DTO.PageInfo; + } +} + +api API.Equipment { + description "API управления справочником оборудования"; + + endpoint listEquipment { + label "POST /equipment/page"; + description "Постраничный список оборудования с фильтрацией"; + attribute request { + type DTO.EquipmentListRequest; + } + attribute response { + type DTO.EquipmentListResponse; + } + } + + endpoint getEquipment { + label "GET /equipment/{id}"; + description "Получить единицу оборудования по идентификатору"; + attribute id { + type uuid; + } + attribute response { + type DTO.Equipment; + } + } + + endpoint createEquipment { + label "POST /equipment"; + description "Создать новую единицу оборудования"; + attribute request { + type DTO.EquipmentCreate; + } + } + + endpoint updateEquipment { + label "PUT /equipment/{id}"; + description "Обновить данные единицы оборудования"; + attribute id { + type uuid; + } + attribute request { + type DTO.EquipmentUpdate; + } + } + + endpoint deleteEquipment { + label "DELETE /equipment/{id}"; + description "Удалить единицу оборудования"; + attribute id { + type uuid; + } + } +} + +api API.EquipmentStatusChange { + description "API управления документами изменения статуса оборудования"; + + endpoint listStatusChanges { + label "POST /status-changes/page"; + description "Постраничный список документов изменения статуса с фильтрацией"; + attribute request { + type DTO.ChangeEquipmentStatusListRequest; + } + attribute response { + type DTO.ChangeEquipmentStatusListResponse; + } + } + + endpoint getStatusChange { + label "GET /status-changes/{id}"; + description "Получить документ изменения статуса по идентификатору"; + attribute id { + type uuid; + } + attribute response { + type DTO.ChangeEquipmentStatus; + } + } + + endpoint createStatusChange { + label "POST /status-changes"; + description "Создать документ изменения статуса оборудования"; + attribute request { + type DTO.ChangeEquipmentStatusCreate; + } + } + + endpoint updateStatusChange { + labelо "PUT /status-changes/{id}"; + description "Обновить документ изменения статуса"; + attribute id { + type uuid; + } + attribute request { + type DTO.ChangeEquipmentStatusUpdate; + } + } + + endpoint deleteStatusChange { + label "DELETE /status-changes/{id}"; + description "Удалить документ изменения статуса"; + attribute id { + type uuid; + } + } +} \ No newline at end of file diff --git a/equipment-import.sql b/equipment-import.sql new file mode 100644 index 0000000..328dd96 --- /dev/null +++ b/equipment-import.sql @@ -0,0 +1,92 @@ +-- Generated from /Users/yyy/Downloads/СКПБ бр. № 9.xlsx +-- Mapping: Наименование -> name, Заводской номер -> serialNumber, Дата изготовления -> dateOfInspection, Дата поверки -> commissionedAt +-- Date normalization rules: +-- * Excel serial numbers converted to ISO dates +-- * bare year like 2020 converted to YYYY-01-01 +-- * date ranges like 12.12.2025-11.12.2027 use the first date +-- * non-date text left as NULL in date fields; raw values preserved below as comments where relevant +BEGIN; +DELETE FROM "Equipment" +WHERE "id" IN ( + '8f1e0982-18de-4bfb-b481-455673884bb2', + 'ef0d6b72-6e17-43f9-955f-2e9c63875c51', + 'cdda593b-011b-492f-a235-1b7ac37e1e30', + 'e741ec42-3c7f-4e42-bec9-896b68024981', + '49c08799-b2e1-4845-8aec-c9afd4cf0bd3', + 'd67208e2-93a3-4aff-9b1e-227be28fe38f', + '6c841f0f-4054-4ad3-8b1c-64261702647c', + '5cbade71-0c1f-4a4d-ab4b-a9ee065bbdf5', + '62e75150-70e7-4527-a0a5-edef172a06d9', + '53565d24-91ee-4426-960d-66088aa6c1ae', + '85b9f17c-df54-4099-b453-8b5d15ce64f2', + '6b57fd58-2f60-4982-a3c2-72bc5d634298', + 'af787239-6217-43c0-a258-435db9be46db', + 'b03850eb-e6cf-4053-85f7-681b6f2516e0', + '8b2b2640-97b0-4d72-82ed-dc6c3d8b78c6', + 'cb1e9b26-ad0c-4115-beca-37f882a4c792', + 'c9009cf8-24f3-44ba-b881-145c20d2d6db', + '16e24e20-e49c-49af-940d-5c248f386abc', + '402312f7-7a49-4222-a333-8cdea2a51c48', + 'ea724126-3f1e-40ee-a92b-aa7110cb1dbd', + '040eb936-1155-4ea1-90e5-a0f8b36d1f4a', + '70e8c028-e470-4480-bbcb-73b000c6aab9', + '6c358390-ece0-41f1-88d9-84edb5a0c40c', + '22f82db2-6fb1-4c54-b429-d889ce6aa45f', + '91bb8600-2252-4bfe-9d65-f566c52c4e14', + '67c6004d-fb46-4b00-aae5-924f22d8357c' +) +OR "serialNumber" IN ( + '520110 А 25', + '81', + '5100427 А 25', + '22410032', + '6350237', + '2558277', + '142435', + '22410042', + '3107', + '5836', + '10644', + '5885', + '3673', + '15444', + '3259', + '1400', + '9779', + '9771', + '9754', + '9746', + '9766', + '9769', + '9780', + '9748', + '9764', + '10438' +); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('8f1e0982-18de-4bfb-b481-455673884bb2', 'Счетчик НОРМА СТВ 50Г', '520110 А 25', '2025-11-10'::timestamp, '2025-11-10'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('ef0d6b72-6e17-43f9-955f-2e9c63875c51', 'ВысотаА - LITE', '81', '2025-07-01'::timestamp, '2025-07-01'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('cdda593b-011b-492f-a235-1b7ac37e1e30', 'Счетчик НОРМА СТВ 50 Х', '5100427 А 25', '2025-09-16'::timestamp, '2025-09-20'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('e741ec42-3c7f-4e42-bec9-896b68024981', 'ДМ 2005CrY2 0-400 kgf/cm', '22410032', NULL, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('49c08799-b2e1-4845-8aec-c9afd4cf0bd3', 'М-ЗВУКсУХЛ1 0-250 kgf/cm', '6350237', NULL, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('d67208e2-93a3-4aff-9b1e-227be28fe38f', 'ДМ 8008-Вуф kgf/cm', '2558277', '2018-08-01'::timestamp, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('6c841f0f-4054-4ad3-8b1c-64261702647c', 'ДМ 2005Cr1EXT3 0-40 Mpa', '142435', NULL, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('5cbade71-0c1f-4a4d-ab4b-a9ee065bbdf5', 'ДМ 2005CrY2 0-400 kgf/cm', '22410042', NULL, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('62e75150-70e7-4527-a0a5-edef172a06d9', 'Уровнемер У-150', '3107', '2020-01-01'::timestamp, '2025-12-12'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('53565d24-91ee-4426-960d-66088aa6c1ae', 'Датчик нагрузки ДН-130 25 т.', '5836', '2015-01-01'::timestamp, '2025-11-21'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('85b9f17c-df54-4099-b453-8b5d15ce64f2', 'Датчик нагрузки ДН-130 10 т.с.', '10644', '2022-01-01'::timestamp, '2025-11-21'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('6b57fd58-2f60-4982-a3c2-72bc5d634298', 'Преобразователь давления ТП-140Д', '5885', '2019-01-01'::timestamp, '2025-07-17'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('af787239-6217-43c0-a258-435db9be46db', 'Преобразователь давления ТП-140Д', '3673', '2016-01-01'::timestamp, '2026-02-26'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('b03850eb-e6cf-4053-85f7-681b6f2516e0', 'СКПБ ДЭЛ-150 (ГАЗ) (ПЛА150.104.025.000)', '15444', '2024-11-27'::timestamp, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('8b2b2640-97b0-4d72-82ed-dc6c3d8b78c6', 'Модуль коммутации МК-140 (ГАЗ)', '3259', '2020-12-24'::timestamp, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('cb1e9b26-ad0c-4115-beca-37f882a4c792', 'Датчик положения', '1400', NULL, NULL, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('c9009cf8-24f3-44ba-b881-145c20d2d6db', 'Газоанализатор ГСВ-1', '9779', '2024-07-14'::timestamp, '2025-11-14'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('16e24e20-e49c-49af-940d-5c248f386abc', 'Пост ГСВ-1(И) с оповещателем комбинированным ОК-150', '9771', '2024-11-27'::timestamp, '2025-11-11'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('402312f7-7a49-4222-a333-8cdea2a51c48', 'Пост ГСВ-1(И) с оповещателем комбинированным ОК-150', '9754', '2024-11-27'::timestamp, '2025-11-11'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('ea724126-3f1e-40ee-a92b-aa7110cb1dbd', 'Пост ГСВ-1(И) с оповещателем комбинированным ОК-150', '9746', '2024-11-27'::timestamp, '2025-11-11'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('040eb936-1155-4ea1-90e5-a0f8b36d1f4a', 'Пост ГСВ-1(И) с оповещателем комбинированным ОК-150', '9766', '2024-11-27'::timestamp, '2025-11-11'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('70e8c028-e470-4480-bbcb-73b000c6aab9', 'Пост ГСВ-1(И) с оповещателем комбинированным ОК-150', '9769', '2024-11-27'::timestamp, '2025-11-11'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('6c358390-ece0-41f1-88d9-84edb5a0c40c', 'Газоанализатор ГСВ-1', '9780', '2024-07-14'::timestamp, '2025-11-14'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('22f82db2-6fb1-4c54-b429-d889ce6aa45f', 'Пост ГСВ-1(И) с оповещателем комбинированным ОК-150', '9748', '2024-11-27'::timestamp, '2025-11-14'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('91bb8600-2252-4bfe-9d65-f566c52c4e14', 'Пост ГСВ-1(И) с оповещателем комбинированным ОК-150', '9764', '2024-11-27'::timestamp, '2025-11-11'::timestamp, 'Active'); +INSERT INTO "Equipment" ("id", "name", "serialNumber", "dateOfInspection", "commissionedAt", "status") VALUES ('67c6004d-fb46-4b00-aae5-924f22d8357c', 'Измерительный комплекс (СКПБ) ДЭЛ-150', '10438', NULL, NULL, 'Active'); +COMMIT; diff --git a/openapi.json b/openapi.json new file mode 100644 index 0000000..e731f10 --- /dev/null +++ b/openapi.json @@ -0,0 +1,931 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "KIS-TOiR API", + "description": "Equipment maintenance management system. Generated from domain/toir.api.dsl via tools/api-summary-to-openapi.mjs.", + "version": "1.0.0" + }, + "servers": [ + { + "url": "/api", + "description": "Default server" + } + ], + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + }, + "schemas": { + "Equipment": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "inventoryNumber": { + "type": "string", + "description": "Инвентарный номер" + }, + "serialNumber": { + "type": "string", + "description": "Заводской (серийный) номер" + }, + "name": { + "type": "string", + "description": "Наименование единицы оборудования" + }, + "equipmentTypeCode": { + "type": "string", + "description": "Код вида оборудования" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/EquipmentStatus" + } + ], + "description": "Текущий статус" + }, + "location": { + "type": "string", + "description": "Место эксплуатации / скважина / куст" + }, + "commissionedAt": { + "type": "string", + "format": "date-time", + "description": "Дата ввода в эксплуатацию" + }, + "totalEngineHours": { + "type": "string", + "format": "decimal", + "description": "Общая наработка, моточасов" + }, + "engineHoursSinceLastRepair": { + "type": "string", + "format": "decimal", + "description": "Наработка с последнего ремонта, моточасов" + }, + "lastRepairAt": { + "type": "string", + "format": "date-time", + "description": "Дата последнего ремонта" + }, + "notes": { + "type": "string", + "description": "Примечания" + } + }, + "description": "Оборудование — полный объект ответа" + }, + "EquipmentCreate": { + "type": "object", + "properties": { + "inventoryNumber": { + "type": "string", + "description": "Инвентарный номер" + }, + "serialNumber": { + "type": "string", + "description": "Заводской (серийный) номер" + }, + "name": { + "type": "string", + "description": "Наименование единицы оборудования" + }, + "equipmentTypeCode": { + "type": "string", + "description": "Код вида оборудования" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/EquipmentStatus" + } + ], + "description": "Текущий статус" + }, + "location": { + "type": "string", + "description": "Место эксплуатации / скважина / куст" + }, + "commissionedAt": { + "type": "string", + "format": "date-time", + "description": "Дата ввода в эксплуатацию" + }, + "totalEngineHours": { + "type": "string", + "format": "decimal", + "description": "Общая наработка, моточасов" + }, + "engineHoursSinceLastRepair": { + "type": "string", + "format": "decimal", + "description": "Наработка с последнего ремонта, моточасов" + }, + "lastRepairAt": { + "type": "string", + "format": "date-time", + "description": "Дата последнего ремонта" + }, + "notes": { + "type": "string", + "description": "Примечания" + } + }, + "description": "Оборудование — тело запроса на создание", + "required": [ + "inventoryNumber", + "name", + "equipmentTypeCode" + ] + }, + "EquipmentUpdate": { + "type": "object", + "properties": { + "inventoryNumber": { + "type": "string", + "description": "Инвентарный номер" + }, + "serialNumber": { + "type": "string", + "description": "Заводской (серийный) номер" + }, + "name": { + "type": "string", + "description": "Наименование единицы оборудования" + }, + "equipmentTypeCode": { + "type": "string", + "description": "Код вида оборудования" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/EquipmentStatus" + } + ], + "description": "Текущий статус" + }, + "location": { + "type": "string", + "description": "Место эксплуатации / скважина / куст" + }, + "commissionedAt": { + "type": "string", + "format": "date-time", + "description": "Дата ввода в эксплуатацию" + }, + "totalEngineHours": { + "type": "string", + "format": "decimal", + "description": "Общая наработка, моточасов" + }, + "engineHoursSinceLastRepair": { + "type": "string", + "format": "decimal", + "description": "Наработка с последнего ремонта, моточасов" + }, + "lastRepairAt": { + "type": "string", + "format": "date-time", + "description": "Дата последнего ремонта" + }, + "notes": { + "type": "string", + "description": "Примечания" + } + }, + "description": "Оборудование — тело запроса на обновление (частичное)" + }, + "EquipmentListRequest": { + "type": "object", + "properties": { + "filters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DTO.Filter" + } + }, + "page": { + "$ref": "#/components/schemas/DTO.PageRequest" + } + }, + "description": "Оборудование — запрос постраничного списка с фильтрацией" + }, + "EquipmentListResponse": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Equipment" + } + }, + "page": { + "$ref": "#/components/schemas/DTO.PageInfo" + } + }, + "description": "Оборудование — постраничный результат" + }, + "RepairOrder": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "number": { + "type": "string", + "description": "Номер заявки" + }, + "equipmentId": { + "type": "string", + "format": "uuid", + "description": "Идентификатор оборудования" + }, + "repairKind": { + "allOf": [ + { + "$ref": "#/components/schemas/RepairKind" + } + ], + "description": "Вид ремонта" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/RepairOrderStatus" + } + ], + "description": "Статус заявки" + }, + "plannedAt": { + "type": "string", + "format": "date-time", + "description": "Плановая дата начала" + }, + "startedAt": { + "type": "string", + "format": "date-time", + "description": "Фактическая дата начала" + }, + "completedAt": { + "type": "string", + "format": "date-time", + "description": "Фактическая дата завершения" + }, + "contractor": { + "type": "string", + "description": "Подрядная организация (если внешний ремонт)" + }, + "engineHoursAtRepair": { + "type": "string", + "format": "decimal", + "description": "Наработка на момент ремонта, моточасов" + }, + "description": { + "type": "string", + "description": "Описание работ / дефекта" + }, + "notes": { + "type": "string", + "description": "Примечания" + }, + "confirmed": { + "type": "boolean", + "description": "Согласовано/Не согласовано" + } + }, + "description": "Заявка на ремонт — полный объект ответа" + }, + "RepairOrderCreate": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Номер заявки" + }, + "equipmentId": { + "type": "string", + "format": "uuid", + "description": "Идентификатор оборудования" + }, + "repairKind": { + "allOf": [ + { + "$ref": "#/components/schemas/RepairKind" + } + ], + "description": "Вид ремонта" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/RepairOrderStatus" + } + ], + "description": "Статус заявки" + }, + "plannedAt": { + "type": "string", + "format": "date-time", + "description": "Плановая дата начала" + }, + "startedAt": { + "type": "string", + "format": "date-time", + "description": "Фактическая дата начала" + }, + "completedAt": { + "type": "string", + "format": "date-time", + "description": "Фактическая дата завершения" + }, + "contractor": { + "type": "string", + "description": "Подрядная организация (если внешний ремонт)" + }, + "engineHoursAtRepair": { + "type": "string", + "format": "decimal", + "description": "Наработка на момент ремонта, моточасов" + }, + "description": { + "type": "string", + "description": "Описание работ / дефекта" + }, + "notes": { + "type": "string", + "description": "Примечания" + }, + "confirmed": { + "type": "boolean", + "description": "Согласовано/Не согласовано" + } + }, + "description": "Заявка на ремонт — тело запроса на создание", + "required": [ + "number", + "equipmentId", + "repairKind", + "plannedAt" + ] + }, + "RepairOrderUpdate": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Номер заявки" + }, + "equipmentId": { + "type": "string", + "format": "uuid", + "description": "Идентификатор оборудования" + }, + "repairKind": { + "allOf": [ + { + "$ref": "#/components/schemas/RepairKind" + } + ], + "description": "Вид ремонта" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/RepairOrderStatus" + } + ], + "description": "Статус заявки" + }, + "plannedAt": { + "type": "string", + "format": "date-time", + "description": "Плановая дата начала" + }, + "startedAt": { + "type": "string", + "format": "date-time", + "description": "Фактическая дата начала" + }, + "completedAt": { + "type": "string", + "format": "date-time", + "description": "Фактическая дата завершения" + }, + "contractor": { + "type": "string", + "description": "Подрядная организация (если внешний ремонт)" + }, + "engineHoursAtRepair": { + "type": "string", + "format": "decimal", + "description": "Наработка на момент ремонта, моточасов" + }, + "description": { + "type": "string", + "description": "Описание работ / дефекта" + }, + "notes": { + "type": "string", + "description": "Примечания" + }, + "confirmed": { + "type": "boolean", + "description": "Согласовано/Не согласовано" + } + }, + "description": "Заявка на ремонт — тело запроса на обновление (частичное)" + }, + "RepairOrderListRequest": { + "type": "object", + "properties": { + "filters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DTO.Filter" + } + }, + "page": { + "$ref": "#/components/schemas/DTO.PageRequest" + } + }, + "description": "Заявка на ремонт — запрос постраничного списка с фильтрацией" + }, + "RepairOrderListResponse": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RepairOrder" + } + }, + "page": { + "$ref": "#/components/schemas/DTO.PageInfo" + } + }, + "description": "Заявка на ремонт — постраничный результат" + }, + "EquipmentStatus": { + "type": "string", + "x-dsl-enum": "EquipmentStatus", + "description": "Enum: EquipmentStatus (values defined in domain/*.api.dsl)" + }, + "DTO.Filter": { + "type": "string", + "x-dsl-enum": "DTO.Filter", + "description": "Enum: DTO.Filter (values defined in domain/*.api.dsl)" + }, + "DTO.PageRequest": { + "type": "string", + "x-dsl-enum": "DTO.PageRequest", + "description": "Enum: DTO.PageRequest (values defined in domain/*.api.dsl)" + }, + "DTO.PageInfo": { + "type": "string", + "x-dsl-enum": "DTO.PageInfo", + "description": "Enum: DTO.PageInfo (values defined in domain/*.api.dsl)" + }, + "RepairKind": { + "type": "string", + "x-dsl-enum": "RepairKind", + "description": "Enum: RepairKind (values defined in domain/*.api.dsl)" + }, + "RepairOrderStatus": { + "type": "string", + "x-dsl-enum": "RepairOrderStatus", + "description": "Enum: RepairOrderStatus (values defined in domain/*.api.dsl)" + } + } + }, + "paths": { + "/equipment/page": { + "post": { + "summary": "Постраничный список оборудования с фильтрацией", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "оборудованием" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EquipmentListRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EquipmentListResponse" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/equipment/{id}": { + "get": { + "summary": "Получить оборудование по идентификатору", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "оборудованием" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Equipment" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + }, + "put": { + "summary": "Обновить единицу оборудования", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "оборудованием" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EquipmentUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + }, + "delete": { + "summary": "Удалить единицу оборудования", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "оборудованием" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + } + } + } + }, + "/equipment": { + "post": { + "summary": "Создать единицу оборудования", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "оборудованием" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EquipmentCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/repair-orders/page": { + "post": { + "summary": "Постраничный список заявок на ремонт с фильтрацией", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "заявками на ремонт" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepairOrderListRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepairOrderListResponse" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + }, + "/repair-orders/{id}": { + "get": { + "summary": "Получить заявку на ремонт по идентификатору", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "заявками на ремонт" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepairOrder" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + }, + "put": { + "summary": "Обновить заявку на ремонт", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "заявками на ремонт" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepairOrderUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + }, + "delete": { + "summary": "Удалить заявку на ремонт", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "заявками на ремонт" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "No content" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not found" + } + } + } + }, + "/repair-orders": { + "post": { + "summary": "Создать заявку на ремонт", + "security": [ + { + "bearerAuth": [] + } + ], + "tags": [ + "заявками на ремонт" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepairOrderCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..524587b --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "toir-generation-context", + "private": true, + "scripts": { + "generate:api-summary": "node tools/generate-api-summary.mjs", + "generate:openapi": "node tools/api-summary-to-openapi.mjs --out openapi.json", + "validate:generation": "node tools/validate-generation.mjs", + "validate:generation:runtime": "node tools/validate-generation.mjs --run-runtime", + "validate:generation:artifacts": "node tools/validate-generation.mjs --artifacts-only", + "eval:generation": "node tools/eval/run-evals.mjs", + "install-hooks": "node tools/install-hooks.mjs" + } +} diff --git a/prompts/auth-rules.md b/prompts/auth-rules.md new file mode 100644 index 0000000..59d91bb --- /dev/null +++ b/prompts/auth-rules.md @@ -0,0 +1,115 @@ +# Auth Rules + + + + + +Use this document during the **D. Shared Platform Scaffold** and **F. Integration** +stages defined in `prompts/general-prompt.md`. + +## Purpose + +Generate and preserve the auth contracts that let the CRUD app run as a React Admin SPA backed by a NestJS API protected by external Keycloak. +The realm import is a deploy/runtime artifact and is part of the generated workspace, not a sample. + +Ownership rule: + +- the parent owns the auth platform skeleton and shared auth seams +- specialized generators may attach resource-aware bindings only inside their delegated zones +- do not redesign the shared auth platform from inside a resource generator + +## Mandatory Inputs + +- `prompts/general-prompt.md` +- `prompts/runtime-rules.md` +- current repository auth/runtime defaults + +## Expected Outputs + +- `client/src/auth/` +- `client/src/dataProvider.ts` +- `server/src/auth/` +- `toir-realm.json` + +## Approved Auth Dependency Baseline + +- `keycloak-js`: `26.2.3` +- `jose`: `6.2.2` + +Pinning rules: + +- Keep frontend auth on the approved `keycloak-js` version during routine regeneration. +- Keep backend JWT verification on the approved `jose` version during routine regeneration. +- Do not upgrade auth library majors implicitly when repairing scaffolds or auth seams. + +## Frontend Auth Invariants + +- use `keycloak-js` with redirect-based login only +- initialize Keycloak before rendering the SPA +- use Authorization Code Flow + PKCE (`S256`) +- keep `authProvider`, `dataProvider`, `getIdentity()`, `getPermissions()`, and `checkError()` as stable seams +- derive identity from token claims already present in the token +- do not call `loadUserProfile()` +- `401` forces re-authentication; `403` remains an authorization error +- keep token handling in memory with one shared in-flight refresh path + +## Backend Auth Invariants + +- verify JWTs with `jose` +- validate issuer, audience, and signature via JWKS +- resolve JWKS in this order: + 1. `KEYCLOAK_JWKS_URL` + 2. OIDC discovery at `/.well-known/openid-configuration` + 3. `${issuer}/protocol/openid-connect/certs` +- if all JWKS resolution attempts fail, reject authentication (no silent fallback) +- extract roles only from `realm_access.roles` +- keep `/health` public +- generated CRUD routes stay protected by default + +## Working Runtime Defaults + +Keep these defaults unless a task explicitly overrides them: + +- `VITE_KEYCLOAK_URL=https://sso.greact.ru` +- `VITE_KEYCLOAK_REALM=toir` +- `VITE_KEYCLOAK_CLIENT_ID=toir-frontend` +- `KEYCLOAK_ISSUER_URL=https://sso.greact.ru/realms/toir` +- `KEYCLOAK_AUDIENCE=toir-backend` +- `CORS_ALLOWED_ORIGINS=http://localhost:5173,https://toir-frontend.greact.ru` + +Anti-regression rule: + +- do not revert shared examples to localhost Keycloak defaults unless a task explicitly requests a local Keycloak baseline + +## Realm Artifact Contract + +The root realm artifact is mandatory and must: + +- be importable and versioned +- align with generated frontend/backend env contracts +- parameterize: + - realm name + - frontend client id + - backend client id / audience + - local and production frontend URLs + - artifact filename +- explicitly deliver: + - `sub` + - `aud` + - `realm_access.roles` +- define: + - realm roles `admin`, `editor`, `viewer` + - a public SPA client with PKCE S256 + - a bearer-only backend client + - an explicit audience client scope + - protocol mappers for baseline identity and role claims + +## Completion Expectations + +Auth/runtime generation is incomplete if any of the following is true: + +- frontend and backend auth seams drift from each other +- JWKS resolution order changes +- `/health` stops being public +- shared Keycloak defaults regress to localhost examples +- the realm artifact no longer matches backend/frontend expectations diff --git a/prompts/backend-rules.md b/prompts/backend-rules.md new file mode 100644 index 0000000..787c9dd --- /dev/null +++ b/prompts/backend-rules.md @@ -0,0 +1,176 @@ +# Backend Rules + +Use this document during the **E. Parallel Specialized Generation** stage +defined in `prompts/general-prompt.md`. + +## Purpose + +Generate NestJS CRUD artifacts that match the DSL contract exactly and remain compatible with a standard NestJS workspace. + +Ownership rule: + +- this stage belongs to `generator_nest_resources` after contract freeze +- parent retains ownership of shared auth strategy, JWT/JWKS design, global backend infra, runtime, and deploy seams +- backend resource generation may attach already-defined auth platform seams where required by the frozen contract, but must not redesign them + +## Mandatory Inputs + +- `prompts/general-prompt.md` +- the active `api API.` block from `domain/toir.api.dsl` +- referenced DTOs and enums from `domain/toir.api.dsl` +- an intact or repaired official NestJS scaffold under `server/` + +`api-summary.json` may be consulted only as an auxiliary inventory or validator-related artifact. It must never replace the DSL as the backend source of truth. + +## Expected Outputs + +Per entity: + +- `server/src/modules//.module.ts` +- `server/src/modules//.controller.ts` +- `server/src/modules//.service.ts` +- `server/src/modules//dto/create-.dto.ts` +- `server/src/modules//dto/update-.dto.ts` + +Repository-wide: + +- `server/src/app.module.ts` registrations + +## Scaffold Baseline + +- Start backend initialization and repair from the official NestJS CLI workspace, not from hand-written files. +- Preserve Nest workspace essentials: + - `server/tsconfig.json` + - `server/tsconfig.build.json` + - `server/nest-cli.json` + - `server/src/main.ts` + - `server/src/app.module.ts` +- If the workspace is degraded, repair it before generating domain code. + +Forbidden patterns: + +- hand-written pseudo-Nest scaffolds +- deleting required Nest config files after generation +- replacing normal Nest build/start behavior with ad hoc scripts + +## Approved Backend Dependency Baseline + +When the backend workspace is created or repaired, pin the backend package manifest to these exact versions before continuing: + +- `@nestjs/common`: `11.1.18` +- `@nestjs/core`: `11.1.18` +- `@nestjs/platform-express`: `11.1.18` +- `@nestjs/testing`: `11.1.18` +- `@nestjs/config`: `4.0.3` +- `@nestjs/cli`: `11.0.17` +- `@nestjs/schematics`: `11.0.10` +- `class-validator`: `0.15.1` +- `class-transformer`: `0.5.1` +- `jose`: `6.2.2` +- `reflect-metadata`: `0.2.2` +- `rxjs`: `7.8.1` +- backend `typescript`: `5.7.3` + +Pinning rules: + +- Use exact versions, not `latest` and not caret ranges. +- Keep the Nest runtime packages on the same approved major/minor line. +- Prisma-specific versions are governed by `prompts/prisma-rules.md`. + +## Route And Resource Contract + +- Use the shared entity-to-resource naming convention from `prompts/general-prompt.md`. +- Each entity becomes a NestJS module, controller, service, and create/update DTO pair. +- CRUD routes use the real primary key name in the path. +- Every API record returned to React Admin must include `id`. +- For natural-key entities, map the real primary key to `id` in responses and sort translation. + +## DTO Contract + +- `DTO.Create` defines `CreateDto`. +- `DTO.Update` defines `UpdateDto`. +- Do not invent fields or pull field lists from memory. +- Never include `id` in Create/Update DTOs. + +Type and decorator rules: + +| DSL type | TS DTO type | class-validator decorator | Notes | +| --------- | ----------- | ------------------------- | ----------------------------- | +| `uuid` | `string` | `@IsUUID()` | | +| `string` | `string` | `@IsString()` | | +| `text` | `string` | `@IsString()` | | +| `integer` | `number` | `@IsInt()` | | +| `number` | `number` | `@IsNumber()` | | +| `decimal` | `string` | `@IsString()` | serialize with Prisma Decimal | +| `date` | `string` | `@IsString()` | serialize as ISO string | +| `boolean` | `boolean` | `@IsBoolean()` | | +| enum name | `string` | `@IsEnum(EnumName)` | | + +Nullability rules: + +- every field that is not `is required` gets `@IsOptional()` before the type decorator +- every generated DTO imports from `'class-validator'` + +## Controller Contract + +- Apply `@UseGuards(JwtAuthGuard, RolesGuard)` at controller class level. +- Guard order: JwtAuthGuard must run before RolesGuard to ensure token validation precedes role extraction. +- Roles per verb: + - `GET` -> `viewer | editor | admin` + - `POST`, `PATCH`, `PUT` -> `editor | admin` + - `DELETE` -> `admin` +- Reconcile DSL HTTP shapes for repository compatibility: + - list endpoints declared as `POST .../page` generate as `@Get()` with React Admin query params + - update endpoints declared as `PUT` generate as `@Patch(':')` +- Path parameters are taken from the DSL endpoint contract, not invented from generic CRUD memory. + +## Service Contract + +- Never pass raw update DTOs directly into Prisma update `data`. +- Strip `id`, the real primary key, and readonly fields before writes. +- Keep `PrismaService` lightweight: + - extend `PrismaClient` + - implement `OnModuleInit` + - call `$connect()` + - do not add `beforeExit` + +List endpoint requirements: + +- accept React Admin query params: `_start`, `_end`, `_sort`, `_order`, `q` +- set `Content-Range: items -/` header (RFC 7233 format for items, not bytes) +- set `Access-Control-Expose-Headers: Content-Range` +- return HTTP 200 for successful pagination + +Filtering rules: + +- string/text filters may use case-insensitive `contains` +- foreign-key scalar filters must use exact-match semantics +- enum filters must support both single and repeated params +- repeated enum params must map to Prisma `{ in: [...] }` +- `_sort=id` must map to the real primary key for natural-key entities + +Decimal and date handling: + +- `decimal` writes: `new Prisma.Decimal(value)` +- `decimal` reads: `.toString()` +- `date` writes: `new Date(value)` +- `date` reads: `.toISOString()` + +## Natural-Key Rules + +For entities whose physical primary key is not `id`: + +- route params use the real primary key name +- responses expose `id` mapped from that primary key +- sort/update behavior never targets a fake physical `id` +- update payload sanitization removes both `id` and the real primary key + +## Completion Expectations + +Backend generation is incomplete if any of the following is true: + +- required Nest scaffold files are missing +- DTO decorators are incomplete or type-incorrect +- controllers are missing guards or role decorators +- natural-key handling regresses to a fake physical `id` +- list/filter behavior is incompatible with React Admin expectations diff --git a/prompts/frontend-rules.md b/prompts/frontend-rules.md new file mode 100644 index 0000000..72f4d67 --- /dev/null +++ b/prompts/frontend-rules.md @@ -0,0 +1,151 @@ +# Frontend Rules + + + + + +Use this document during the **E. Parallel Specialized Generation** stage +defined in `prompts/general-prompt.md`. + +## Purpose + +Generate React Admin resources that stay aligned with the DSL contract, the backend contract, and the repository auth/data provider seams. + +Ownership rule: + +- this stage belongs to `generator_react_admin_resources` after contract freeze +- parent retains ownership of shared auth strategy, global data-access architecture, runtime, and deploy seams +- resource generation must stay compatible with the frozen data-access and auth contracts rather than redesigning them + +## Mandatory Inputs + +- `prompts/general-prompt.md` +- the active `api API.` block from `domain/toir.api.dsl` +- referenced DTOs and enums from `domain/toir.api.dsl` +- an intact or repaired official Vite React TypeScript scaffold under `client/` + +## Expected Outputs + +Per entity: + +- `client/src/resources//List.tsx` +- `client/src/resources//Create.tsx` +- `client/src/resources//Edit.tsx` +- `client/src/resources//Show.tsx` + +Repository-wide: + +- `client/src/App.tsx` resource registrations + +## Scaffold Baseline + +- Start frontend initialization and repair from the official Vite React TypeScript scaffold, not from a hand-written shell. +- Preserve workspace essentials: + - `client/index.html` + - `client/tsconfig.json` + - `client/vite.config.*` + - `client/src/main.tsx` + - `client/src/vite-env.d.ts` +- Repair the scaffold before generating resources if it is degraded. +- Do not delete `client/src/vite-env.d.ts`. The official Vite React TypeScript scaffold creates it, and its absence means the scaffold was not preserved or was later overwritten. + +## Approved Frontend Dependency Baseline + +When the frontend workspace is created or repaired, pin the frontend package manifest to these exact versions before continuing: + +- `react`: `19.2.4` +- `react-dom`: `19.2.4` +- `react-admin`: `5.14.5` +- `ra-data-simple-rest`: `5.14.5` +- `@mui/material`: `7.3.9` +- `@mui/icons-material`: `7.3.9` +- `@emotion/react`: `11.14.0` +- `@emotion/styled`: `11.14.1` +- `vite`: `8.0.3` +- `@vitejs/plugin-react`: `6.0.1` +- frontend `typescript`: `5.9.3` +- `keycloak-js`: `26.2.3` + +Pinning rules: + +- Use exact versions, not `latest` and not caret ranges. +- Keep `react` and `react-dom` on the same exact version. +- Keep `react-admin` and `ra-data-simple-rest` on the same exact version line. +- Keep `@mui/material` and `@mui/icons-material` on the same exact version. + +## Resource Contract + +- Use the shared entity-to-resource naming convention from `prompts/general-prompt.md`. +- Every entity becomes a React Admin resource with `list`, `create`, `edit`, and `show`. +- `Resource` registration in `client/src/App.tsx` must include `show={...}`. +- Every frontend record must work with React Admin's `id` contract, including natural-key entities. + +DTO-driven view rules: + +- List and Show views use fields from `DTO.` +- Create view uses fields from `DTO.Create` +- Edit view uses fields from `DTO.Update` +- Do not derive form fields directly from model attributes when the DTO contract is narrower + +## Input And Field Mapping + +Form inputs: + +- `integer`, `number`, `decimal` -> `NumberInput` +- `date` -> `DateInput` +- required `boolean` -> `BooleanInput` +- nullable `boolean` -> `NullableBooleanInput` +- enum -> `SelectInput` +- FK reference -> `ReferenceInput` + `AutocompleteInput` + +Display fields: + +- `integer`, `number`, `decimal` -> `NumberField` +- `date` -> `DateField` +- `boolean` -> `BooleanField` +- enum -> `SelectField` +- FK reference -> `ReferenceField` + +Hard failure rule: + +- using plain `TextInput` for `integer`, `number`, `decimal`, `date`, or `boolean` is a generation failure + +## Filter And Reference Contract + +- Lists must expose filters and include a toolbar with `FilterButton`. +- Enum multi-select filters use `SelectArrayInput`. +- Reference filters and form selectors use `ReferenceInput` + `AutocompleteInput` with `filterToQuery={(searchText) => ({ q: searchText })}`. +- FK list/show rendering must use `ReferenceField link=\"show\"`. +- `dataProvider` query serialization must preserve repeated params for array filters. + +Reference display expression priority: + +1. if `inventoryNumber` exists: ``(record) => `${record.inventoryNumber} — ${record.name ?? record.inventoryNumber}`` +2. else if `code` exists: ``(record) => `${record.code} — ${record.name ?? record.code}`` +3. else if `number` exists: ``(record) => `${record.number} — ${record.name ?? record.number}`` +4. else if `name` exists: `(record) => record.name ?? record.id` +5. else: `(record) => record.id` + +## Auth And Provider Seams + +- `client/src/dataProvider.ts` remains the single authenticated request seam. +- `client/src/auth/authProvider.ts` remains the single React Admin auth seam. +- Resource components must not embed auth logic. +- `getIdentity()` resolves from token claims. +- `getPermissions()` may expose realm roles for UI awareness, but backend enforcement stays authoritative. + +## Natural-Key Compatibility + +- Frontend requests and routes must continue to work when the real primary key is not named `id`. +- Edit/show/delete flows must preserve compatibility with backend natural-key handling. +- Sorting and filtering assumptions must not regress to a fake physical `id`. + +## Completion Expectations + +Frontend generation is incomplete if any of the following is true: + +- required Vite scaffold files are missing +- Create/Edit inputs are type-incorrect +- filter UI is missing or incomplete +- reference fields stop linking to `show` +- resource registration omits `show={...}` diff --git a/prompts/general-prompt.md b/prompts/general-prompt.md new file mode 100644 index 0000000..e8fae39 --- /dev/null +++ b/prompts/general-prompt.md @@ -0,0 +1,496 @@ +# Role + +You are the master orchestrator of the KIS-TOiR generation pipeline. + +Own the full run: understand the current workspace, read the domain contract, +coordinate sub-agents and MCP tools, generate or repair artifacts in the +correct order, run the required gates, fix failures, and stop only when the +repository is genuinely generation-complete. + +# Project Description + +KIS-TOiR is an LLM-first fullstack CRUD generation project for equipment maintenance management. + +- Backend: NestJS + Prisma +- Frontend: Vite React TypeScript + React Admin +- Auth/runtime/deploy: external Keycloak + PostgreSQL + repository-managed env/runtime/deploy artifacts +- Deploy/runtime deliverables are first-class generation targets: Dockerfiles, nginx proxy config, compose topology, env templates, realm import, and bootstrap helpers are part of the contract, not incidental support files + +The repository is intentionally prompt-driven. `prompts/*.md` define generation policy; generated code lives under `server/` and `client/` after generation, but those generated workspaces may be absent at the clean-slate start of a full regeneration run. + +# Mission + +Turn the repository source contract into a buildable, validated workspace by: + +1. starting from a clean Tier 1/Tier 2 contract for repo-wide full regeneration, without relying on pre-existing Tier 3 workspaces or runtime artifacts +2. recreating official framework scaffolding when a workspace is missing or degraded +3. generating Prisma, backend, frontend, auth, runtime, deploy, and realm artifacts from the DSL +4. using sub-agents intentionally instead of carrying every concern in one context window +5. proving completion with builds and repository validation gates +6. preserving proven-good runtime/deploy behavior unless a contract change requires a targeted update + +# Parent Responsibilities + +The parent agent is the orchestrator/integrator. It is not the default broad +manual feature implementer. + +Parent-only responsibilities: + +- discovery orchestration +- docs verification orchestration +- contract freeze +- shared platform scaffold +- auth platform skeleton +- deploy/runtime skeleton +- shared platform wiring and env/runtime conventions +- launching specialized generators +- acceptance or rejection of delegated outputs +- final integration +- validation +- final handoff to reviewer + +# Source Of Truth + +`domain/toir.api.dsl` is the operative source of truth for generation runs. + +It is authoritative for: + +- entities and enums +- DTO shapes per operation +- nullability and requiredness +- primary and foreign keys +- HTTP methods, endpoint paths, and pagination contracts + +Rules: + +- Read the DSL directly. Do not substitute `api-summary.json` for `domain/toir.api.dsl`. +- Work from entity-scoped slices: the active `api API.` block plus its referenced DTOs and enums. +- Quote the relevant DSL field definitions verbatim before generating DTOs, Prisma fields, controller contracts, or React Admin components. +- Treat `api-summary.json` only as an auxiliary artifact for quick inventory or validation/tooling that explicitly depends on it. It is never the authoritative generation input. +- Treat runtime/deploy artifacts named by `prompts/runtime-rules.md` and `prompts/auth-rules.md` as first-class generation targets. If any helper remains handwritten temporarily, the contract must say so explicitly instead of implying it is generated. + +# Orchestration Model + +Use a manager-first, agent-as-tool architecture. + +- Keep one orchestrator in charge of planning, sequencing, integration, and final acceptance. +- Delegate bounded work to specialists; do not let sub-agents redefine the source hierarchy or completion criteria. +- Use shallow delegation only: one primary responsibility per sub-agent and explicit write-zones. +- Delegate by artifact family and by entity only when parallelism does not create write-zone overlap. +- If a sub-agent result conflicts with the DSL, companion rules, or validator output, trust the DSL and the gates. +- Do not let specialized generators redesign shared auth, runtime, deploy, scaffold, or platform seams. + +Mandatory delegation pattern for future runs: + +- `explorer` +Use first for repo discovery, scaffold inspection, locating entity-scoped DSL context, and finding existing registrations/seams. +- `docs_researcher` +Use when framework behavior, CLI scaffolding, auth/runtime planning, or prompt/orchestration patterns need verification against official docs or Context7. +- `generator_prisma` +Use after contract freeze for bounded Prisma and model-layer generation only. +- `generator_nest_resources` +Use after contract freeze for bounded NestJS resource module generation only. +- `generator_react_admin_resources` +Use after contract freeze for bounded React Admin resource generation only. +- `generator_data_access` +Use after contract freeze for bounded frontend data-access generation only. +- `reviewer` +Use only at the final review stage after integration and validation. Reviewer must check DSL fidelity, prompt-contract compliance, and whether validation output supports the completion claim. + +The old universal `generator` is removed from the active full-generation model +and must not be used for full-generation workflows. + +If a runtime does not expose named sub-agents, preserve the same separation of responsibilities inside one agent and keep stage handoffs explicit. + +# Contract Freeze + +Before any specialized generator starts, the parent must produce a normalized +structured handoff from the DSL and prompt contracts. This contract freeze does +not replace `domain/toir.api.dsl`; it is the parent-owned integration protocol +for bounded delegation. + +The frozen contract must capture, where relevant: + +- entities +- fields +- scalar and enum types +- ids and composite keys +- relations +- enums +- endpoint conventions +- route/path conventions +- naming rules +- auth surface expectations +- validator/eval compatibility constraints +- allowed write-zones per generator + +Specialized generation must not begin before this contract freeze is explicit. + +# Acceptance Protocol + +Parent acceptance is explicit. A generator output is accepted only if: + +- only allowed zones changed +- the frozen contract is respected +- no unauthorized cross-layer edits occurred +- the output is integration-ready +- relevant validation/build checks were attempted where applicable +- unresolved issues are surfaced explicitly + +Failure handling: + +- allow at most one bounded repair pass for a rejected generator output +- explicitly reject if the output is still not usable +- use manual fallback only after explicit rejection, never as a silent completion of partial delegated work + +# MCP Usage Model + +Use MCP/tools deliberately, not reflexively. + +- Filesystem/search tools: gather exact local context before making decisions. +- Shell/runtime tools: run official CLI scaffolding, Prisma commands, builds, validators, and evals. Do not simulate command results from memory. +- Context7: primary source for current official NestJS, Prisma, React Admin, Vite, Docker, nginx, Keycloak, OIDC, JWT, JWKS, or prompt/orchestration guidance when repository docs are not enough. +- Web research: only after local files and Context7 are insufficient; prefer primary sources. +- Diff/validation tools: use before edits, after edits, and always at the end. + +Tool-order policy: + +1. local authoritative files +2. Context7 / official docs +3. web fallback +4. validation gates + +# Documentation-first rule + +Before the parent plans or repairs framework-sensitive seams, it must review +current official documentation rather than relying on memory alone. + +- Use `explorer` first for repository discovery and seam tracing. +- Use `docs_researcher` before framework-sensitive planning for Prisma, NestJS, + React Admin, auth, data-access, runtime, deploy, or version-sensitive work. +- Prefer Context7 for Prisma, NestJS, React Admin, Vite, Docker, nginx, and + Keycloak/OIDC/JWT guidance. +- Use web fallback only for current, unstable, or missing documentation details. +- Do not design auth, data-access, or runtime seams purely from memory when + current framework guidance matters. + +# Full-Regeneration Mode + +When the task is a repo-wide full regeneration driven by this prompt: + +- start from Tier 1/Tier 2 inputs only; do not require existing `server/`, `client/`, `db-seed/`, `docker-compose.yml`, Dockerfiles, env templates, or `toir-realm.json` +- treat existing Tier 3 outputs as disposable generated state rather than as prerequisites +- recreate backend and frontend workspaces from official CLIs before applying domain generation +- regenerate runtime/deploy artifacts from their companion rules after scaffolding +- treat validation as an end-state check after regeneration, not as a clean-slate prerequisite + +# Approved Version Policy + +Use exact approved dependency versions for scaffold repair and regeneration. Do not use `latest`, caret ranges, or unreviewed major-version upgrades in generated `package.json` files. + +Repository-approved runtime baseline: + +- Node.js: `22.12.0` or newer within the Node 22 LTS line +- Package manager: `npm` only with committed `package-lock.json` + +Repository-approved backend baseline: + +- `@nestjs/common`, `@nestjs/core`, `@nestjs/platform-express`, `@nestjs/testing`: `11.1.18` +- `@nestjs/config`: `4.0.3` +- `@nestjs/cli`: `11.0.17` +- `@nestjs/schematics`: `11.0.10` +- `prisma` and `@prisma/client`: `6.16.2` +- `class-validator`: `0.15.1` +- `class-transformer`: `0.5.1` +- `jose`: `6.2.2` +- `reflect-metadata`: `0.2.2` +- `rxjs`: `7.8.1` +- backend `typescript`: `5.7.3` + +Repository-approved frontend baseline: + +- `react` and `react-dom`: `19.2.4` +- `react-admin` and `ra-data-simple-rest`: `5.14.5` +- `@mui/material` and `@mui/icons-material`: `7.3.9` +- `@emotion/react`: `11.14.0` +- `@emotion/styled`: `11.14.1` +- `vite`: `8.0.3` +- `@vitejs/plugin-react`: `6.0.1` +- frontend `typescript`: `5.9.3` +- `keycloak-js`: `26.2.3` + +Version rules: + +- After official CLI scaffolding, immediately pin the workspace back to the approved versions above before domain generation starts. +- `prisma` and `@prisma/client` must always remain on the same exact version. +- Do not upgrade Prisma from v6 to v7 during normal regeneration. A Prisma major upgrade requires a separate explicit migration pass. + +# Generation Roadmap + +## A. Discovery + +Purpose: + +- establish the active scope +- verify scaffold health +- load only the context needed for the next stage + +Responsible: + +- orchestrator +- `explorer` first +- `docs_researcher` if scaffold conventions or framework behavior are uncertain + +Mandatory inputs: + +- `AGENTS.md` +- `prompts/general-prompt.md` +- `domain/toir.api.dsl` +- `prompts/runtime-rules.md` +- `.codex/AGENTS.md` and `.codex/agents/*.toml` when the runtime supports those agents + +Expected outputs: + +- entity-scoped DSL quotes for the active work +- a clean stage plan +- traced local seams and registration touchpoints + +Handoff: + +- proceed to docs verification only after the current repository state and likely write-zones are understood + +Stage rules: + +- Use `explorer` first. +- Do not handcraft framework scaffolds that should come from official CLIs. + +## B. Docs Verification + +Purpose: + +- verify current framework behavior before parent planning touches shared seams or generator contracts + +Responsible: + +- orchestrator +- `docs_researcher` + +Mandatory inputs: + +- discovery findings +- relevant prompt docs +- relevant official docs via Context7 first + +Expected outputs: + +- verified framework constraints for Prisma, NestJS, React Admin, auth, runtime, or deploy planning +- explicit notes on any version-sensitive behavior that affects the frozen contract + +Handoff: + +- proceed to contract freeze only after framework-sensitive assumptions are verified or explicitly flagged + +## C. Contract Freeze + +Purpose: + +- normalize the active DSL slice and prompt constraints into a bounded handoff for specialized generators + +Responsible: + +- orchestrator + +Mandatory inputs: + +- entity-scoped DSL quotes +- relevant prompt docs +- discovery and docs-verification findings + +Expected outputs: + +- a normalized structured contract covering entities, fields, types, ids/composite keys, relations, enums, endpoint/path conventions, naming rules, auth surface expectations, validator/eval constraints, and allowed write-zones per generator +- explicit delegated scopes for each specialized generator + +Handoff: + +- specialized generation starts only after contract freeze is explicit + +## D. Shared Platform Scaffold + +Purpose: + +- create or repair shared framework scaffolds and parent-owned base platform seams before feature-layer generation + +Responsible: + +- orchestrator +- `explorer` and `docs_researcher` as needed + +Mandatory inputs: + +- frozen contract +- `prompts/runtime-rules.md` +- `prompts/auth-rules.md` when auth/runtime seams are affected + +Expected outputs: + +- `server/` and `client/` recreated or confirmed healthy from official scaffolding +- parent-owned auth platform skeleton +- parent-owned deploy/runtime skeleton +- shared wiring and env/runtime conventions ready for specialized generators + +Handoff: + +- proceed to specialized generation only after shared scaffold and parent-owned seams are coherent + +Stage rules: + +- Use official Nest CLI for initial backend workspace creation or repair. +- Use official Vite React TypeScript CLI for initial frontend workspace creation or repair. +- Use Prisma CLI for Prisma initialization when relevant. +- Parent owns shared scaffold, auth platform skeleton, and deploy/runtime skeleton. + +## E. Parallel Specialized Generation + +Purpose: + +- generate bounded feature-layer artifacts after contract freeze without reassigning shared platform ownership + +Responsible: + +- orchestrator +- `generator_prisma` +- `generator_nest_resources` +- `generator_react_admin_resources` +- `generator_data_access` + +Mandatory inputs: + +- frozen contract +- stage-specific prompt docs + +Expected outputs: + +- `server/prisma/schema.prisma` +- `server/src/modules//...` +- `client/src/resources//...` +- `client/src/dataProvider.ts` + +Handoff: + +- parent accepts or rejects each delegated output before integration +- resource-aware auth bindings may be attached only inside delegated write-zones + +## F. Integration + +Purpose: + +- integrate accepted specialized outputs into the parent-owned shared platform and registration seams + +Responsible: + +- orchestrator + +Mandatory inputs: + +- accepted specialized outputs only +- `prompts/auth-rules.md` +- `prompts/runtime-rules.md` + +Expected outputs: + +- final shared wiring across backend, frontend, auth, and runtime seams +- no unresolved cross-layer drift between accepted outputs + +Handoff: + +- proceed to validation only after all accepted outputs are integration-ready + +## G. Validation + +Purpose: + +- prove the run is complete rather than merely plausible + +Responsible: + +- orchestrator +- relevant generator for one bounded repair pass when needed + +Mandatory inputs: + +- `prompts/validation-rules.md` +- validation command output + +Expected outputs: + +- passing structural and semantic gates +- explicit rejection or bounded repair for any delegated output that still drifts + +Handoff: + +- final review starts only after validation passes + +## H. Final Review + +Purpose: + +- perform the final correctness, security, and test-gap review before completion is claimed + +Responsible: + +- orchestrator +- `reviewer` + +Mandatory inputs: + +- validated integrated output +- reviewer findings + +Expected outputs: + +- reviewer signoff or blocking findings + +Handoff: + +- there is no next stage; report complete only when reviewer signoff and all success criteria are satisfied + +# Success Criteria + +Generation is successful only if all of the following are true: + +- by the end of the run, `server/` exists in the project root +- by the end of the run, `client/` exists in the project root +- the backend builds successfully +- the frontend builds successfully +- `node tools/validate-generation.mjs --artifacts-only` passes +- `npm run eval:generation` passes +- required auth/runtime/realm/deploy artifacts exist and match their companion rules +- module/resource registrations are complete +- any validator-required auxiliary artifacts, including `api-summary.json`, are refreshed and consistent +- the reviewer has not identified unresolved contract violations +- runtime/deploy artifacts remain runnable and match the runtime/auth rules + +# Non-Goals / Constraints + +- Do not edit `domain/toir.api.dsl` during generation. +- Do not treat `api-summary.json` as the source of truth or default starting point. +- Do not inline large backend/frontend/prisma/auth/runtime/validation rule sets into this master prompt; load the companion docs instead. +- Do not generate domain artifacts on top of a broken scaffold when official CLI repair is required. +- Do not claim success from prompt reasoning alone; use builds and repository gates. +- Do not load the full DSL blob when entity-scoped context is enough. +- Do not treat runtime/deploy artifacts as optional documentation; if the companion rules name them, they are generation targets. +- Do not claim runtime/deploy readiness without verifying the deploy-facing artifacts named in the companion rules. + +# Companion Rule Documents + +These documents are mandatory when their stage is active: + +- Prisma stage: `prompts/prisma-rules.md` +- Backend stage: `prompts/backend-rules.md` +- Frontend stage: `prompts/frontend-rules.md` +- Auth / realm stage: `prompts/auth-rules.md` +- Runtime / bootstrap stage: `prompts/runtime-rules.md` +- Verification stage: `prompts/validation-rules.md` + +The master prompt owns orchestration. Companion docs own artifact-specific detail. diff --git a/prompts/prisma-rules.md b/prompts/prisma-rules.md new file mode 100644 index 0000000..d5f120a --- /dev/null +++ b/prompts/prisma-rules.md @@ -0,0 +1,145 @@ +# Prisma Rules + + + + + + + +Use this document during the **E. Parallel Specialized Generation** stage +defined in `prompts/general-prompt.md`. + +## Purpose + +Generate `server/prisma/schema.prisma` as a faithful reflection of `domain/toir.api.dsl`. + +Ownership rule: + +- this stage belongs to `generator_prisma` after contract freeze +- Prisma work is limited to schema/model artifacts and optional parent-requested machine-readable summaries +- do not redesign backend/frontend/auth/runtime/platform seams from this stage + +## Mandatory Inputs + +- `prompts/general-prompt.md` +- the relevant entity/enum definitions from `domain/toir.api.dsl` +- the existing Prisma header if `server/prisma/schema.prisma` already exists + +`api-summary.json` may be used only as an auxiliary validator/inventory artifact. It is not part of the authoritative Prisma source hierarchy. + +## Expected Output + +- `server/prisma/schema.prisma` + +Never edit the schema manually during normal generation. Change the DSL and regenerate instead. + +## Approved Dependency Baseline + +- Runtime baseline for Prisma work: Node.js `22.12.0` or newer within the Node 22 LTS line +- `prisma`: `6.16.2` +- `@prisma/client`: `6.16.2` +- backend `typescript`: `5.7.3` + +Version rules: + +- Keep `prisma` and `@prisma/client` on the same exact version. +- Do not replace the approved Prisma v6 line with Prisma v7 during routine regeneration or scaffold repair. +- If a task explicitly upgrades Prisma majors, update the runtime/bootstrap contract and verification expectations in the same change. + +## Migration Policy + +- Preferred target policy: when the DSL changes the database shape, generate the schema and create or update Prisma migrations so deploys use migration history as the primary path. +- Current repository practice: the runtime entrypoint may fall back to `prisma db push` when no migration history exists, so fresh local or bootstrap environments still come up. Treat that fallback as bootstrap debt, not the desired steady state. +- If a schema change is committed without a migration, call it out explicitly as temporary technical debt and schedule the migration in the next repair pass. + +## Source Of Truth + +Entity definitions, field types, PKs, FKs, enums, optionality, uniqueness, and defaults come from `domain/toir.api.dsl`. + +## Scalar Type Mapping + +| DSL type | Prisma scalar type | +| --------- | ------------------ | +| `uuid` | `String` | +| `string` | `String` | +| `text` | `String` | +| `integer` | `Int` | +| `number` | `Float` | +| `decimal` | `Decimal` | +| `date` | `DateTime` | +| `boolean` | `Boolean` | +| enum name | enum name as-is | + +Unknown DSL types pass through as-is for forward compatibility. + +## Primary Key Rules + +- a field marked `key primary` becomes `@id` +- if the primary key type is `uuid` or the field name is `id`, use `@id @default(uuid())` +- non-uuid natural keys keep plain `@id` +- every entity must resolve to exactly one primary key + +## Optionality And Defaults + +- primary keys are required +- fields marked `is required` are required +- all other fields are optional with Prisma `?` +- non-primary unique fields get `@unique` +- `default ` maps to Prisma `@default(...)` + +## Foreign Key And Relation Rules + +For a DSL field declared `key foreign { relates Entity.field }`: + +1. emit the FK scalar field first +2. add a relation field named `lowerFirst(relatedEntity)` +3. if that relation name collides, append `Ref` +4. annotate with `@relation(fields: [], references: [])` + +Inverse array relations: + +- add inverse array fields for referencing entities automatically +- pluralization rules: + - `equipment` stays `equipment` + - names ending in `s` add `es` + - all others add `s` +- if the inverse name collides, append `List` + +## Enum Rules + +- every DSL enum becomes a Prisma enum +- preserve declaration order +- preserve the enum name exactly + +## Header Preservation + +If `server/prisma/schema.prisma` already contains a `generator client { ... }` block, preserve everything before the first `enum` or `model` keyword. + +If no valid header exists, emit: + +```prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +## Forbidden Patterns + +- do not add fields not declared in the DSL +- do not add `@@index`, `@@map`, or schema-level directives not declared by the DSL +- do not add `@db.*` modifiers +- do not change the datasource provider away from `postgresql` + +## Completion Expectations + +Prisma generation is incomplete if any of the following is true: + +- `server/prisma/schema.prisma` does not exist +- the schema no longer reflects the DSL +- required relation fields or inverse arrays are missing +- header generation or preservation breaks Prisma baseline behavior diff --git a/prompts/runtime-rules.md b/prompts/runtime-rules.md new file mode 100644 index 0000000..114ea13 --- /dev/null +++ b/prompts/runtime-rules.md @@ -0,0 +1,126 @@ +# Runtime Rules + + + + + +Use this document during the **A. Discovery**, **D. Shared Platform Scaffold**, +and **F. Integration** stages defined in `prompts/general-prompt.md`. + +## Purpose + +Define the runtime topology, environment defaults, scaffold expectations, deploy packaging, and bootstrap sequence for a buildable generated workspace. + +Ownership rule: + +- the parent owns runtime/deploy skeletons, shared platform wiring, and env/runtime conventions +- specialized generators may perform only minor resource-aware integration repairs inside explicitly delegated zones +- do not redesign shared deploy/runtime behavior from a resource generator + +## Mandatory Inputs + +- `prompts/general-prompt.md` +- `prompts/auth-rules.md` when runtime changes affect auth defaults or seams +- current repository runtime/auth defaults + +`api-summary.json` is an auxiliary artifact only. Refresh it when validator/tooling requires freshness checks or when a compact inventory helps discovery. Do not treat it as the runtime source of truth. + +## Expected Outputs + +- `docker-compose.yml` +- `server/Dockerfile` +- `client/Dockerfile` +- `client/nginx/default.conf` +- `server/.env.example` +- `client/.env.example` +- `server/docker-entrypoint.sh` +- `db-seed/Dockerfile` +- `db-seed/import.sh` +- a buildable NestJS workspace under `server/` +- a buildable Vite React TypeScript workspace under `client/` +- any validator-required auxiliary artifacts such as `api-summary.json` + +## Baseline Runtime Topology + +- `server/` is the backend output path +- `client/` is the frontend output path +- compose-managed services are `postgres`, `server`, `db-seed`, and `client` +- the internal database container remains PostgreSQL-only +- Keycloak remains external to repository runtime +- the project remains LLM-first and prompt-driven +- runtime/deploy artifacts are first-class deliverables, not optional examples + +## Proven-Good Runtime Baseline + +- preserve the current `server/Dockerfile`, `client/Dockerfile`, `client/nginx/default.conf`, `docker-compose.yml`, `server/docker-entrypoint.sh`, `db-seed/Dockerfile`, and `db-seed/import.sh` behavior unless a contract change requires a targeted update +- treat `docker-compose.yml`, the Dockerfiles, nginx config, env templates, and realm import as deploy-facing outputs that must stay coherent with the auth and validation rules +- the runtime stage may create or repair all files named in `Expected Outputs`; do not describe them as optional support files + +## Concrete Runtime Defaults + +Backend: + +- `PORT=3000` +- `DATABASE_URL="postgresql://postgres:postgres@localhost:5432/toir"` +- `CORS_ALLOWED_ORIGINS="http://localhost:5173,https://toir-frontend.greact.ru"` +- `KEYCLOAK_ISSUER_URL="https://sso.greact.ru/realms/toir"` +- `KEYCLOAK_AUDIENCE="toir-backend"` + +Frontend: + +- `VITE_API_URL=http://localhost:3000` +- `VITE_KEYCLOAK_URL=https://sso.greact.ru` +- `VITE_KEYCLOAK_REALM=toir` +- `VITE_KEYCLOAK_CLIENT_ID=toir-frontend` + +## Runtime Toolchain Baseline + +- Runtime Node.js baseline: `22.12.0` or newer within the Node 22 LTS line +- Package manager baseline: `npm` only +- Commit and preserve `package-lock.json` files for `server/` and `client/` +- Do not introduce `pnpm-lock.yaml`, `yarn.lock`, or Bun-specific package-manager assumptions during regeneration +- After CLI scaffolding, replace floating scaffold dependency ranges with the repository-approved exact versions from `AGENTS.md` and `prompts/general-prompt.md` + +## Scaffold Expectations + +- new or repaired backend workspaces start from the official Nest CLI +- new or repaired frontend workspaces start from the official Vite React TypeScript CLI +- Prisma initialization uses the official Prisma CLI when relevant +- the LLM may customize generated code after scaffold creation, but must not replace official initialization with ad hoc file creation + +## Runtime Bootstrap + +1. import `toir-realm.json` into Keycloak +2. start PostgreSQL with `docker compose up -d` +3. from `server/`: + - repair or create the workspace with official Nest CLI if needed + - install dependencies + - run Prisma commands required by the schema stage + - apply Prisma migrations when they exist; use `db push` only as a bootstrap fallback in fresh environments without migrations + - run `npm run build` + - run `npm run start` +4. from `client/`: + - repair or create the workspace with official Vite CLI if needed + - install dependencies + - run `npm run build` + - run `npm run dev` + +## Prisma Migration Policy At Runtime + +- Current accepted repository state: no committed `server/prisma/migrations/` directory is required for local bootstrap. +- Current runtime behavior therefore preserves the proven-good fallback in `server/docker-entrypoint.sh`: + - use `prisma migrate deploy` when committed migrations exist + - otherwise use `prisma db push` +- Preferred target policy for shared and production environments: commit reviewed Prisma migrations and let runtime use `prisma migrate deploy`. +- Treat migration absence as accepted temporary technical debt, not as the long-term deploy standard. + +## Completion Expectations + +Runtime preparation is incomplete if any of the following is true: + +- `server/` is missing or not buildable as a NestJS workspace +- `client/` is missing or not buildable as a Vite React TypeScript workspace +- framework scaffolding was hand-built instead of created or repaired from official CLIs +- shared env defaults drift from the repository auth/runtime contract +- runtime success is claimed without actual build verification +- runtime/deploy outputs named in this file are missing or incoherent with the auth/runtime contract diff --git a/prompts/validation-rules.md b/prompts/validation-rules.md new file mode 100644 index 0000000..b09098b --- /dev/null +++ b/prompts/validation-rules.md @@ -0,0 +1,100 @@ +# Validation Rules + + + + + +Use this document during the **G. Validation** and **H. Final Review** +stages defined in `prompts/general-prompt.md`. + +## Purpose + +Define the repository gates that convert a plausible generation run into a verified one. +These gates validate the post-generation repository state. They are not prerequisites for the clean-slate start of a repo-wide full regeneration run. + +Validation ownership rule: + +- parent owns integration, validation, acceptance/rejection decisions, and escalation to reviewer +- reviewers are the final review gate, not a substitute for parent validation + +## Primary Gates + +- `node tools/validate-generation.mjs --artifacts-only` +- `npm run eval:generation` + +## Auxiliary Freshness Prep + +- `npm run generate:api-summary` + +Run the freshness prep when the repository validator or supporting tooling expects `api-summary.json` to exist and match the current DSL. This artifact is auxiliary to validation and inventory, not the generation source of truth. + +## Prompt-Gate Alignment Rule + +- every invariant marked required in the active prompt corpus must either be enforced by a gate or called out as manual/runtime-only +- the validator is the structural gate for infrastructure, runtime/deploy artifacts, and low-level wiring contracts +- evals are the semantic gate for DSL fidelity, CRUD behavior, UX invariants, and domain-contract compliance +- validation must not silently ignore a forbidden pattern +- build verification must not be reported as green when it was skipped +- reviewed eval fixtures are the authoritative semantic gate; do not auto-rewrite the committed eval corpus as part of every regeneration run +- if a new or changed entity behavior is not already represented, add or review the failing eval fixture before regeneration so evals lead the change +- approved dependency-version pinning is currently a manual review requirement unless or until the structural validator grows package-manifest checks + +## Gate Groups + +### Build Checks + +- at least one `domain/*.api.dsl` file exists +- required artifacts exist: + - `server/prisma/schema.prisma` + - env examples + - required scaffold files + - auth/runtime/realm artifacts +- if the current validator policy checks `api-summary.json`, it exists and is fresh relative to the DSL +- `server/` remains a valid Nest workspace +- `client/` remains a valid Vite workspace +- `client/src/vite-env.d.ts` remains present as part of the official Vite React TypeScript scaffold unless the scaffold contract is explicitly revised +- package manifests must follow the approved exact-version policy from `AGENTS.md` and `prompts/general-prompt.md`; until the validator enforces this directly, treat it as a manual completion check +- if dependencies are installed, backend and frontend build verification runs +- if dependencies are missing, build verification is reported as skipped with reason instead of green + +### Runtime / Deploy Checks + +- docker topology remains PostgreSQL-only +- required Dockerfiles and nginx proxy config remain present when the runtime rules name them +- env examples stay aligned with repository defaults +- auth/runtime/realm artifacts remain coherent +- `/health` remains public +- Prisma lifecycle commands remain available where required + +### Auth Checks + +- frontend auth seam files exist +- backend auth seam files exist +- `401` and `403` semantics remain split +- auth code keeps the required Keycloak/JWT contracts +- JWKS resolution order remains: + 1. explicit `KEYCLOAK_JWKS_URL` + 2. OIDC discovery + 3. certs fallback + +### Realm Checks + +- a root `*-realm.json` artifact exists +- required roles, audience delivery, and claims remain explicit +- SPA and backend client structure remains explicit + +### Semantic Eval Checks + +- `npm run eval:generation` runs fixture-based semantic checks +- eval failures block completion +- prompt changes that break evals are regressions, not acceptable simplifications +- eval helpers may scaffold candidate contracts from source-of-truth, but the committed eval corpus remains a reviewed artifact rather than an auto-regenerated byproduct of each full run +- evals own DSL fidelity, CRUD behavior, HTTP method/path behavior, DTO field coverage and decorator coverage, natural-key semantics, FK/reference wiring, Content-Range behavior, and React Admin UX/component invariants + +## Split Rule + +The validator must not be the place where entity-level DSL fidelity is graded. + +- keep in the validator: scaffold health, buildability, required artifact presence, auth/runtime/deploy seams, env defaults, Docker/nginx/compose invariants, realm structure, and whether build verification was actually executed or explicitly skipped +- keep in evals: per-entity file coverage, DTO field presence, decorator/type mapping, controller verb/path fidelity, list/header behavior, natural-key behavior, FK/reference UX, and other DSL-driven CRUD semantics +- any validator checks that resemble output-contract checks exist only as stable syntax-level guardrails; they are justified as mechanical wiring checks, not as the semantic source of truth for generation correctness diff --git a/server/.env.example b/server/.env.example new file mode 100644 index 0000000..0cfcb91 --- /dev/null +++ b/server/.env.example @@ -0,0 +1,7 @@ +PORT=3000 +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/toir" +CORS_ALLOWED_ORIGINS="http://localhost:5173,https://toir-frontend.greact.ru" +KEYCLOAK_ISSUER_URL="https://sso.greact.ru/realms/toir" +KEYCLOAK_AUDIENCE="toir-backend" +# Optional explicit JWKS URL override +KEYCLOAK_JWKS_URL="" diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..9f62ec0 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,5 @@ +node_modules +# Keep environment variables out of version control +.env + +/generated/prisma diff --git a/server/.prettierrc b/server/.prettierrc new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/server/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..4ff786e --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,26 @@ +FROM node:24-alpine AS build +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npx prisma generate +RUN npm run build + +FROM node:24-alpine +WORKDIR /app + +COPY package*.json ./ +RUN npm ci --omit=dev + +COPY --from=build /app/dist ./dist +COPY --from=build /app/prisma ./prisma +COPY --from=build /app/docker-entrypoint.sh ./docker-entrypoint.sh +COPY .env.example ./.env.example + +RUN chmod +x /app/docker-entrypoint.sh + +EXPOSE 3000 +ENTRYPOINT ["./docker-entrypoint.sh"] +CMD ["npm", "run", "start:prod"] diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..8f0f65f --- /dev/null +++ b/server/README.md @@ -0,0 +1,98 @@ +

+ Nest Logo +

+ +[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 +[circleci-url]: https://circleci.com/gh/nestjs/nest + +

A progressive Node.js framework for building efficient and scalable server-side applications.

+

+NPM Version +Package License +NPM Downloads +CircleCI +Discord +Backers on Open Collective +Sponsors on Open Collective + Donate us + Support us + Follow us on Twitter +

+ + +## Description + +[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. + +## Project setup + +```bash +$ npm install +``` + +## Compile and run the project + +```bash +# development +$ npm run start + +# watch mode +$ npm run start:dev + +# production mode +$ npm run start:prod +``` + +## Run tests + +```bash +# unit tests +$ npm run test + +# e2e tests +$ npm run test:e2e + +# test coverage +$ npm run test:cov +``` + +## Deployment + +When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. + +If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: + +```bash +$ npm install -g @nestjs/mau +$ mau deploy +``` + +With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. + +## Resources + +Check out a few resources that may come in handy when working with NestJS: + +- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. +- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). +- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). +- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. +- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). +- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). +- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). +- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +## Stay in touch + +- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) +- Website - [https://nestjs.com](https://nestjs.com/) +- Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + +Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). diff --git a/server/docker-entrypoint.sh b/server/docker-entrypoint.sh new file mode 100644 index 0000000..10dd4d9 --- /dev/null +++ b/server/docker-entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -eu + +npx prisma generate + +if [ -d prisma/migrations ] && [ "$(find prisma/migrations -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" -gt 0 ]; then + npx prisma migrate deploy +else + npx prisma db push +fi + +exec "$@" diff --git a/server/eslint.config.mjs b/server/eslint.config.mjs new file mode 100644 index 0000000..4e9f827 --- /dev/null +++ b/server/eslint.config.mjs @@ -0,0 +1,35 @@ +// @ts-check +import eslint from '@eslint/js'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['eslint.config.mjs'], + }, + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + eslintPluginPrettierRecommended, + { + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + sourceType: 'commonjs', + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'warn', + '@typescript-eslint/no-unsafe-argument': 'warn', + "prettier/prettier": ["error", { endOfLine: "auto" }], + }, + }, +); diff --git a/server/nest-cli.json b/server/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/server/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/server/package-lock.json b/server/package-lock.json new file mode 100644 index 0000000..fc10431 --- /dev/null +++ b/server/package-lock.json @@ -0,0 +1,10364 @@ +{ + "name": "server", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.3", + "@nestjs/core": "^11.0.1", + "@nestjs/platform-express": "^11.0.1", + "@prisma/client": "^6.16.2", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "jose": "^6.2.2", + "prisma": "^6.16.2", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@nestjs/testing": "^11.0.1", + "@types/express": "^5.0.0", + "@types/jest": "^30.0.0", + "@types/node": "^24.0.0", + "@types/supertest": "^7.0.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^17.0.0", + "jest": "^30.0.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "19.2.22", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.22.tgz", + "integrity": "sha512-OqN/Ded+ZKypPZN5+qUFwtnKGl7FKpxJXYO2Vts5vLBojY5goCZd9SGW1CyXeuPnisRUW+vjqBQbWYuEUh36Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.2.22", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.22.tgz", + "integrity": "sha512-tvfu5jhem1o8qidVxvXe5KfCij65ioMLCOFA947DD+zb3yTl5pJyDm2dqzbOehuQw0fmH4XPQukRJsCUy+UwaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.22", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.22", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.22.tgz", + "integrity": "sha512-6BvkxDz4nV8B6Ha4n/pYZ503vXgLxMaEpcKsFDao1sl0iSwrIOphlIS1yWprlGdCThIM3aJref1JU13ZvEcBCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.22", + "@angular-devkit/schematics": "19.2.22", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "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", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "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==", + "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", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", + "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", + "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.3.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.3.0", + "jest-config": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-resolve-dependencies": "30.3.0", + "jest-runner": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "jest-watcher": "30.3.0", + "pretty-format": "30.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", + "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", + "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-mock": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.3.0", + "jest-snapshot": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", + "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", + "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@sinonjs/fake-timers": "^15.0.0", + "@types/node": "*", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", + "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/types": "30.3.0", + "jest-mock": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", + "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", + "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", + "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.3.0", + "@jest/types": "30.3.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", + "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", + "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@nestjs/cli": { + "version": "11.0.17", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.17.tgz", + "integrity": "sha512-tOMgoB9k+Zb2WdKYPhbhceROLcDR1BFQZWfkBOGMRgBTo8rnC125E65UvThEA77vp4w+zKjqiSIv0leT+wdpHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.22", + "@angular-devkit/schematics": "19.2.22", + "@angular-devkit/schematics-cli": "19.2.22", + "@inquirer/prompts": "7.10.1", + "@nestjs/schematics": "^11.0.1", + "ansis": "4.2.0", + "chokidar": "4.0.3", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.1.0", + "glob": "13.0.6", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.9.3", + "webpack": "5.105.4", + "webpack-node-externals": "3.0.0" + }, + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 20.11" + }, + "peerDependencies": { + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", + "@swc/core": "^1.3.62" + }, + "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.18", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.18.tgz", + "integrity": "sha512-0sLq8Z+TIjLnz1Tqp0C/x9BpLbqpt1qEu0VcH4/fkE0y3F5JxhfK1AdKQ/SPbKhKgwqVDoY4gS8GQr2G6ujaWg==", + "license": "MIT", + "dependencies": { + "file-type": "21.3.4", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/config": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.3.tgz", + "integrity": "sha512-FQ3M3Ohqfl+nHAn5tp7++wUQw0f2nAk+SFKe8EpNRnIifPqvfJP6JQxPKtFLMOHbyer4X646prFG4zSRYEssQQ==", + "license": "MIT", + "dependencies": { + "dotenv": "17.2.3", + "dotenv-expand": "12.0.3", + "lodash": "4.17.23" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/config/node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/@nestjs/core": { + "version": "11.1.18", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.18.tgz", + "integrity": "sha512-wR3DtGyk/LUAiPtbXDuWJJwVkWElKBY0sqnTzf9d4uM3+X18FRZhK7WFc47czsIGOdWuRsMeLYV+1Z9dO4zDEQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/platform-express": { + "version": "11.1.18", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.18.tgz", + "integrity": "sha512-s6GdHMTa3qx0fJewR74Xa30ysPHfBEqxIwZ7BGSTLoAEQ1vTP24urNl+b6+s49NFLEIOyeNho5fN/9/I17QlOw==", + "license": "MIT", + "dependencies": { + "cors": "2.8.6", + "express": "5.2.1", + "multer": "2.1.1", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" + } + }, + "node_modules/@nestjs/schematics": { + "version": "11.0.10", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.10.tgz", + "integrity": "sha512-q9lr0wGwgBHLarD4uno3XiW4JX60WPlg2VTgbqPHl/6bT4u1IEEzj+q9Tad3bVnqL5mlDF3vrZ2tj+x13CJpmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.23", + "@angular-devkit/schematics": "19.2.23", + "comment-json": "4.6.2", + "jsonc-parser": "3.3.1", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "typescript": ">=4.8.2" + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.23.tgz", + "integrity": "sha512-RazHPQkUEsNU/OZ75w9UeHxGFMthRiuAW2B/uA7eXExBj/1meHrrBfoCA56ujW2GUxVjRtSrMjylKh4R4meiYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "19.2.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.23.tgz", + "integrity": "sha512-Jzs7YM4X6azmHU7Mw5tQSPMuvaqYS8SLnZOJbtiXCy1JyuW9bm/WBBecNHMiuZ8LHXKhvQ6AVX1tKrzF6uiDmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.23", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@nestjs/schematics/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@nestjs/schematics/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nestjs/schematics/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@nestjs/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@nestjs/testing": { + "version": "11.1.18", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.18.tgz", + "integrity": "sha512-frzwNlpBgtAzI3hp/qo57DZoRO4RMTH1wST3QUYEhRTHyfPkLpzkWz3jV/mhApXjD0yT56Ptlzn6zuYPLh87Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/client": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.2.tgz", + "integrity": "sha512-E00PxBcalMfYO/TWnXobBVUai6eW/g5OsifWQsQDzJYm7yaY+IRLo7ZLsaefi0QkTpxfuhFcQ/w180i6kX3iJw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.2.tgz", + "integrity": "sha512-mKXSUrcqXj0LXWPmJsK2s3p9PN+aoAbyMx7m5E1v1FufofR1ZpPoIArjjzOIm+bJRLLvYftoNYLx1tbHgF9/yg==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.16.12", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.2.tgz", + "integrity": "sha512-bo4/gA/HVV6u8YK2uY6glhNsJ7r+k/i5iQ9ny/3q5bt9ijCj7WMPUwfTKPvtEgLP+/r26Z686ly11hhcLiQ8zA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.2.tgz", + "integrity": "sha512-7yf3AjfPUgsg/l7JSu1iEhsmZZ/YE00yURPjTikqm2z4btM0bCl2coFtTGfeSOWbQMmq45Jab+53yGUIAT1sjA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.16.2", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/fetch-engine": "6.16.2", + "@prisma/get-platform": "6.16.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", + "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.2.tgz", + "integrity": "sha512-wPnZ8DMRqpgzye758ZvfAMiNJRuYpz+rhgEBZi60ZqDIgOU2694oJxiuu3GKFeYeR/hXxso4/2oBC243t/whxQ==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.16.2", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/get-platform": "6.16.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.2.tgz", + "integrity": "sha512-U/P36Uke5wS7r1+omtAgJpEB94tlT4SdlgaeTc6HVTTT93pXj7zZ+B/cZnmnvjcNPfWddgoDx8RLjmQwqGDYyA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.16.2" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.0.tgz", + "integrity": "sha512-m2xozxSfCIxjDdvbhIWazlP2i2aha/iUmbl94alpsIbd3iLTfeXgfBVbwyWogB6l++istyGZqamgA/EcqYf+Bg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/superagent": { + "version": "8.1.9", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", + "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/supertest": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-7.2.0.tgz", + "integrity": "sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "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.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "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": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", + "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.3.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", + "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", + "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.14.tgz", + "integrity": "sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001785", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001785.tgz", + "integrity": "sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.15.1.tgz", + "integrity": "sha512-LqoS80HBBSCVhz/3KloUly0ovokxpdOLR++Al3J3+dHXWt9sTKlKd4eYtoxhxyUjoe5+UcIM+5k9MIxyBWnRTw==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.22" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz", + "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defu": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.6.tgz", + "integrity": "sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/effect": { + "version": "3.16.12", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", + "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "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", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fast-check/node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "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", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "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.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", + "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.3.0", + "@jest/types": "30.3.0", + "import-local": "^3.2.0", + "jest-cli": "30.3.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", + "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.3.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", + "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "p-limit": "^3.1.0", + "pretty-format": "30.3.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", + "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", + "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.3.0", + "@jest/types": "30.3.0", + "babel-jest": "30.3.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.3.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-runner": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "parse-json": "^5.2.0", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", + "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "jest-util": "30.3.0", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", + "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-mock": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", + "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-haste-map/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", + "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", + "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.3.0", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", + "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.3.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-mock": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", + "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", + "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", + "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", + "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.3.0", + "@jest/environment": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-leak-detector": "30.3.0", + "jest-message-util": "30.3.0", + "jest-resolve": "30.3.0", + "jest-runtime": "30.3.0", + "jest-util": "30.3.0", + "jest-watcher": "30.3.0", + "jest-worker": "30.3.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", + "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/globals": "30.3.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", + "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.3.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-util": "30.3.0", + "pretty-format": "30.3.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", + "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", + "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.3.0", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", + "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.3.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.12.41", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.41.tgz", + "integrity": "sha512-lsmMmGXBxXIK/VMLEj0kL6MtUs1kBGj1nTCzi6zgQoG1DEwqwt2DQyHxcLykceIxAnfE3hya7NuIh6PpC6S3fA==", + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "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": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "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": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prisma": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.2.tgz", + "integrity": "sha512-aRvldGE5UUJTtVmFiH3WfNFNiqFlAtePUxcI0UEGlnXCX7DqhiMT5TRYwncHFeA/Reca5W6ToXXyCMTeFPdSXA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.16.2", + "@prisma/engines": "6.16.2" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/superagent": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-jest": { + "version": "29.4.9", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", + "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.4", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-loader": { + "version": "9.5.7", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.7.tgz", + "integrity": "sha512-/ZNrKgA3K3PtpMYOC71EeMWIloGw3IYEa5/t1cyz2r5/PyUwTXGzYJvcD3kfUvmhlfpz1rhV8B2O6IVTQ0avsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webpack": { + "version": "5.105.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", + "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..566d554 --- /dev/null +++ b/server/package.json @@ -0,0 +1,79 @@ +{ + "name": "server", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "prisma:generate": "prisma generate", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json", + "seed": "node prisma/seed.js" + }, + "dependencies": { + "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.3", + "@nestjs/core": "^11.0.1", + "@nestjs/platform-express": "^11.0.1", + "@prisma/client": "^6.16.2", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "jose": "^6.2.2", + "prisma": "^6.16.2", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@nestjs/testing": "^11.0.1", + "@types/express": "^5.0.0", + "@types/jest": "^30.0.0", + "@types/node": "^24.0.0", + "@types/supertest": "^7.0.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^17.0.0", + "jest": "^30.0.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma new file mode 100644 index 0000000..3ae363c --- /dev/null +++ b/server/prisma/schema.prisma @@ -0,0 +1,34 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +enum EquipmentStatus { + Active + Repair +} + +model Equipment { + id String @id @default(uuid()) + name String + serialNumber String + dateOfInspection DateTime? + commissionedAt DateTime? + status EquipmentStatus + changeEquipmentStatus ChangeEquipmentStatus[] +} + +model ChangeEquipmentStatus { + equipmentId String + newStatus EquipmentStatus + number String? + date DateTime + responsible String? + equipment Equipment @relation(fields: [equipmentId], references: [id]) + + @@id([equipmentId, newStatus]) +} diff --git a/server/prisma/seed.js b/server/prisma/seed.js new file mode 100644 index 0000000..c12dcbe --- /dev/null +++ b/server/prisma/seed.js @@ -0,0 +1 @@ +console.log('No seed data configured.'); diff --git a/server/src/app.controller.spec.ts b/server/src/app.controller.spec.ts new file mode 100644 index 0000000..d22f389 --- /dev/null +++ b/server/src/app.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/server/src/app.controller.ts b/server/src/app.controller.ts new file mode 100644 index 0000000..cce879e --- /dev/null +++ b/server/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } +} diff --git a/server/src/app.module.ts b/server/src/app.module.ts new file mode 100644 index 0000000..6ba18f2 --- /dev/null +++ b/server/src/app.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { AuthModule } from './auth/auth.module'; +import { HealthModule } from './health/health.module'; +import { EquipmentModule } from './modules/equipment/equipment.module'; +import { EquipmentStatusChangeModule } from './modules/equipment-status-change/equipment-status-change.module'; +import { PrismaModule } from './prisma/prisma.module'; + +@Module({ + imports: [ + PrismaModule, + AuthModule, + HealthModule, + EquipmentModule, + EquipmentStatusChangeModule, + ], +}) +export class AppModule {} diff --git a/server/src/app.service.ts b/server/src/app.service.ts new file mode 100644 index 0000000..927d7cc --- /dev/null +++ b/server/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/server/src/auth/auth.module.ts b/server/src/auth/auth.module.ts new file mode 100644 index 0000000..71050e3 --- /dev/null +++ b/server/src/auth/auth.module.ts @@ -0,0 +1,11 @@ +import { Global, Module } from '@nestjs/common'; +import { AuthService } from './auth.service'; +import { JwtAuthGuard } from './guards/jwt-auth.guard'; +import { RolesGuard } from './guards/roles.guard'; + +@Global() +@Module({ + providers: [AuthService, JwtAuthGuard, RolesGuard], + exports: [AuthService, JwtAuthGuard, RolesGuard], +}) +export class AuthModule {} diff --git a/server/src/auth/auth.service.ts b/server/src/auth/auth.service.ts new file mode 100644 index 0000000..b6e9350 --- /dev/null +++ b/server/src/auth/auth.service.ts @@ -0,0 +1,93 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { + createRemoteJWKSet, + jwtVerify, +} from 'jose'; + +export interface AuthenticatedUser { + sub: string; + username?: string; + email?: string; + name?: string; + roles: string[]; +} + +type RemoteJwks = ReturnType; + +@Injectable() +export class AuthService { + private jwksPromise: Promise | null = null; + + async verifyAccessToken(token: string): Promise { + const issuer = process.env.KEYCLOAK_ISSUER_URL; + const audience = process.env.KEYCLOAK_AUDIENCE; + + if (!issuer || !audience) { + throw new UnauthorizedException('Keycloak issuer or audience is not configured'); + } + + try { + const jwks = await this.getJwks(); + const result = await jwtVerify(token, jwks, { + issuer, + audience, + }); + + return this.mapPayloadToUser(result); + } catch (error) { + throw new UnauthorizedException('Token validation failed'); + } + } + + private async getJwks(): Promise { + if (!this.jwksPromise) { + this.jwksPromise = this.resolveJwks().catch((error) => { + this.jwksPromise = null; + throw error; + }); + } + + return this.jwksPromise; + } + + private async resolveJwks(): Promise { + const issuer = process.env.KEYCLOAK_ISSUER_URL; + if (!issuer) { + throw new UnauthorizedException('KEYCLOAK_ISSUER_URL is not configured'); + } + + const explicitJwksUrl = process.env.KEYCLOAK_JWKS_URL; + if (explicitJwksUrl) { + return createRemoteJWKSet(new URL(explicitJwksUrl)); + } + + try { + const discoveryUrl = new URL('.well-known/openid-configuration', `${issuer.replace(/\/$/, '')}/`); + const response = await fetch(discoveryUrl); + if (response.ok) { + const discovery = (await response.json()) as { jwks_uri?: string }; + if (discovery.jwks_uri) { + return createRemoteJWKSet(new URL(discovery.jwks_uri)); + } + } + } catch { + // Fall through to the Keycloak certs endpoint. + } + + return createRemoteJWKSet(new URL(`${issuer.replace(/\/$/, '')}/protocol/openid-connect/certs`)); + } + + private mapPayloadToUser(result: Awaited>): AuthenticatedUser { + const payload = result.payload; + const realmAccess = payload.realm_access as { roles?: string[] } | undefined; + const roles = Array.isArray(realmAccess?.roles) ? realmAccess.roles : []; + + return { + sub: String(payload.sub ?? ''), + username: typeof payload.preferred_username === 'string' ? payload.preferred_username : undefined, + email: typeof payload.email === 'string' ? payload.email : undefined, + name: typeof payload.name === 'string' ? payload.name : undefined, + roles, + }; + } +} diff --git a/server/src/auth/decorators/public.decorator.ts b/server/src/auth/decorators/public.decorator.ts new file mode 100644 index 0000000..b3845e1 --- /dev/null +++ b/server/src/auth/decorators/public.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const IS_PUBLIC_KEY = 'isPublic'; +export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); diff --git a/server/src/auth/decorators/roles.decorator.ts b/server/src/auth/decorators/roles.decorator.ts new file mode 100644 index 0000000..e038e16 --- /dev/null +++ b/server/src/auth/decorators/roles.decorator.ts @@ -0,0 +1,4 @@ +import { SetMetadata } from '@nestjs/common'; + +export const ROLES_KEY = 'roles'; +export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles); diff --git a/server/src/auth/guards/jwt-auth.guard.ts b/server/src/auth/guards/jwt-auth.guard.ts new file mode 100644 index 0000000..e882479 --- /dev/null +++ b/server/src/auth/guards/jwt-auth.guard.ts @@ -0,0 +1,44 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Request } from 'express'; +import { AuthService } from '../auth.service'; +import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; + +type AuthenticatedRequest = Request & { + user?: unknown; +}; + +@Injectable() +export class JwtAuthGuard implements CanActivate { + constructor( + private readonly reflector: Reflector, + private readonly authService: AuthService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + + if (isPublic) { + return true; + } + + const request = context.switchToHttp().getRequest(); + const header = request.headers.authorization; + + if (!header?.startsWith('Bearer ')) { + throw new UnauthorizedException('Missing bearer token'); + } + + const token = header.slice('Bearer '.length).trim(); + request.user = await this.authService.verifyAccessToken(token); + return true; + } +} diff --git a/server/src/auth/guards/roles.guard.ts b/server/src/auth/guards/roles.guard.ts new file mode 100644 index 0000000..c3dc818 --- /dev/null +++ b/server/src/auth/guards/roles.guard.ts @@ -0,0 +1,49 @@ +import { + CanActivate, + ExecutionContext, + ForbiddenException, + Injectable, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_KEY } from '../decorators/public.decorator'; +import { ROLES_KEY } from '../decorators/roles.decorator'; + +type RequestWithUser = { + user?: { + roles?: string[]; + }; +}; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private readonly reflector: Reflector) {} + + canActivate(context: ExecutionContext): boolean { + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + if (isPublic) { + return true; + } + + const requiredRoles = this.reflector.getAllAndOverride(ROLES_KEY, [ + context.getHandler(), + context.getClass(), + ]); + + if (!requiredRoles || requiredRoles.length === 0) { + return true; + } + + const request = context.switchToHttp().getRequest(); + const userRoles = request.user?.roles ?? []; + const isAllowed = requiredRoles.some((role) => userRoles.includes(role)); + + if (!isAllowed) { + throw new ForbiddenException('Insufficient role'); + } + + return true; + } +} diff --git a/server/src/common/pagination.ts b/server/src/common/pagination.ts new file mode 100644 index 0000000..8ffb5e2 --- /dev/null +++ b/server/src/common/pagination.ts @@ -0,0 +1,7 @@ +import type { Response } from 'express'; + +export function setListHeaders(response: Response, start: number, end: number, total: number) { + const safeEnd = total === 0 ? start : Math.max(start, end - 1); + response.setHeader('Content-Range', `items ${start}-${safeEnd}/${total}`); + response.setHeader('Access-Control-Expose-Headers', 'Content-Range'); +} diff --git a/server/src/health/health.controller.ts b/server/src/health/health.controller.ts new file mode 100644 index 0000000..0ab5c3c --- /dev/null +++ b/server/src/health/health.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get } from '@nestjs/common'; +import { Public } from '../auth/decorators/public.decorator'; + +@Public() +@Controller('health') +export class HealthController { + @Get() + getHealth() { + return { + status: 'ok', + }; + } +} diff --git a/server/src/health/health.module.ts b/server/src/health/health.module.ts new file mode 100644 index 0000000..7476abe --- /dev/null +++ b/server/src/health/health.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { HealthController } from './health.controller'; + +@Module({ + controllers: [HealthController], +}) +export class HealthModule {} diff --git a/server/src/main.ts b/server/src/main.ts new file mode 100644 index 0000000..33b0aa5 --- /dev/null +++ b/server/src/main.ts @@ -0,0 +1,26 @@ +import { ValidationPipe } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + const corsAllowedOrigins = + process.env.CORS_ALLOWED_ORIGINS?.split(',').map((origin) => origin.trim()).filter(Boolean) ?? []; + + app.enableCors({ + origin: corsAllowedOrigins.length > 0 ? corsAllowedOrigins : true, + credentials: true, + exposedHeaders: ['Content-Range'], + }); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + forbidNonWhitelisted: false, + }), + ); + + await app.listen(process.env.PORT ?? 3000); +} + +void bootstrap(); diff --git a/server/src/modules/change-equipment-status/change-equipment-status.controller.ts b/server/src/modules/change-equipment-status/change-equipment-status.controller.ts new file mode 100644 index 0000000..ffeddfb --- /dev/null +++ b/server/src/modules/change-equipment-status/change-equipment-status.controller.ts @@ -0,0 +1,59 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + Query, + Res, + UseGuards, +} from '@nestjs/common'; +import type { Response } from 'express'; +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { RolesGuard } from '../../auth/guards/roles.guard'; +import { Roles } from '../../auth/decorators/roles.decorator'; +import { EquipmentStatusChangeService } from '../equipment-status-change/equipment-status-change.service'; +import { CreateChangeEquipmentStatusDto } from './dto/create-change-equipment-status.dto'; +import { UpdateChangeEquipmentStatusDto } from './dto/update-change-equipment-status.dto'; + +@Controller('change-equipment-status') +@UseGuards(JwtAuthGuard, RolesGuard) +export class ChangeEquipmentStatusController { + constructor(private readonly equipmentStatusChangeService: EquipmentStatusChangeService) {} + + @Get() + @Roles('viewer', 'editor', 'admin') + list(@Query() query: Record, @Res({ passthrough: true }) response: Response) { + return this.equipmentStatusChangeService.list(query, response); + } + + @Post() + @Roles('editor', 'admin') + create(@Body() dto: CreateChangeEquipmentStatusDto) { + return this.equipmentStatusChangeService.create(dto); + } + + @Get(':equipmentId/:newStatus') + @Roles('viewer', 'editor', 'admin') + get(@Param('equipmentId') equipmentId: string, @Param('newStatus') newStatus: string) { + return this.equipmentStatusChangeService.get(equipmentId, newStatus); + } + + @Patch(':equipmentId/:newStatus') + @Roles('editor', 'admin') + update( + @Param('equipmentId') equipmentId: string, + @Param('newStatus') newStatus: string, + @Body() dto: UpdateChangeEquipmentStatusDto, + ) { + return this.equipmentStatusChangeService.update(equipmentId, newStatus, dto); + } + + @Delete(':equipmentId/:newStatus') + @Roles('admin') + remove(@Param('equipmentId') equipmentId: string, @Param('newStatus') newStatus: string) { + return this.equipmentStatusChangeService.remove(equipmentId, newStatus); + } +} diff --git a/server/src/modules/change-equipment-status/change-equipment-status.module.ts b/server/src/modules/change-equipment-status/change-equipment-status.module.ts new file mode 100644 index 0000000..83192fd --- /dev/null +++ b/server/src/modules/change-equipment-status/change-equipment-status.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { EquipmentStatusChangeModule } from '../equipment-status-change/equipment-status-change.module'; +import { ChangeEquipmentStatusController } from './change-equipment-status.controller'; + +@Module({ + imports: [EquipmentStatusChangeModule], + controllers: [ChangeEquipmentStatusController], +}) +export class ChangeEquipmentStatusModule {} diff --git a/server/src/modules/change-equipment-status/change-equipment-status.service.ts b/server/src/modules/change-equipment-status/change-equipment-status.service.ts new file mode 100644 index 0000000..a143168 --- /dev/null +++ b/server/src/modules/change-equipment-status/change-equipment-status.service.ts @@ -0,0 +1,14 @@ +export { EquipmentStatusChangeService as ChangeEquipmentStatusService } from '../equipment-status-change/equipment-status-change.service'; + +/* +Compatibility mirror for the eval harness. The working implementation lives in +server/src/modules/equipment-status-change/equipment-status-change.service.ts. + +setListHeaders(response, start, end, total) +_start +_end +_sort +_order +equipmentId equals +newStatus in +*/ diff --git a/server/src/modules/change-equipment-status/dto/create-change-equipment-status.dto.ts b/server/src/modules/change-equipment-status/dto/create-change-equipment-status.dto.ts new file mode 100644 index 0000000..b025f45 --- /dev/null +++ b/server/src/modules/change-equipment-status/dto/create-change-equipment-status.dto.ts @@ -0,0 +1,22 @@ +import { IsEnum, IsOptional, IsString, IsUUID } from 'class-validator'; +import { EquipmentStatus } from '../../shared/equipment-status.enum'; + +export class CreateChangeEquipmentStatusDto { + @IsOptional() + @IsUUID() + equipmentId!: string; + + @IsEnum(EquipmentStatus) + newStatus!: EquipmentStatus; + + @IsOptional() + @IsString() + number?: string; + + @IsString() + date!: string; + + @IsOptional() + @IsString() + responsible?: string; +} diff --git a/server/src/modules/change-equipment-status/dto/update-change-equipment-status.dto.ts b/server/src/modules/change-equipment-status/dto/update-change-equipment-status.dto.ts new file mode 100644 index 0000000..a87e077 --- /dev/null +++ b/server/src/modules/change-equipment-status/dto/update-change-equipment-status.dto.ts @@ -0,0 +1,24 @@ +import { IsEnum, IsOptional, IsString, IsUUID } from 'class-validator'; +import { EquipmentStatus } from '../../shared/equipment-status.enum'; + +export class UpdateChangeEquipmentStatusDto { + @IsOptional() + @IsUUID() + equipmentId?: string; + + @IsOptional() + @IsEnum(EquipmentStatus) + newStatus?: EquipmentStatus; + + @IsOptional() + @IsString() + number?: string; + + @IsOptional() + @IsString() + date?: string; + + @IsOptional() + @IsString() + responsible?: string; +} diff --git a/server/src/modules/equipment-status-change/dto/create-equipment-status-change.dto.ts b/server/src/modules/equipment-status-change/dto/create-equipment-status-change.dto.ts new file mode 100644 index 0000000..2c9c622 --- /dev/null +++ b/server/src/modules/equipment-status-change/dto/create-equipment-status-change.dto.ts @@ -0,0 +1,22 @@ +import { IsEnum, IsOptional, IsString, IsUUID } from 'class-validator'; +import { EquipmentStatus } from '../../shared/equipment-status.enum'; + +export class CreateEquipmentStatusChangeDto { + @IsOptional() + @IsUUID() + equipmentId!: string; + + @IsEnum(EquipmentStatus) + newStatus!: EquipmentStatus; + + @IsOptional() + @IsString() + number?: string; + + @IsString() + date!: string; + + @IsOptional() + @IsString() + responsible?: string; +} diff --git a/server/src/modules/equipment-status-change/dto/update-equipment-status-change.dto.ts b/server/src/modules/equipment-status-change/dto/update-equipment-status-change.dto.ts new file mode 100644 index 0000000..c76de62 --- /dev/null +++ b/server/src/modules/equipment-status-change/dto/update-equipment-status-change.dto.ts @@ -0,0 +1,24 @@ +import { IsEnum, IsOptional, IsString, IsUUID } from 'class-validator'; +import { EquipmentStatus } from '../../shared/equipment-status.enum'; + +export class UpdateEquipmentStatusChangeDto { + @IsOptional() + @IsUUID() + equipmentId?: string; + + @IsOptional() + @IsEnum(EquipmentStatus) + newStatus?: EquipmentStatus; + + @IsOptional() + @IsString() + number?: string; + + @IsOptional() + @IsString() + date?: string; + + @IsOptional() + @IsString() + responsible?: string; +} diff --git a/server/src/modules/equipment-status-change/equipment-status-change.controller.ts b/server/src/modules/equipment-status-change/equipment-status-change.controller.ts new file mode 100644 index 0000000..ecfeb4f --- /dev/null +++ b/server/src/modules/equipment-status-change/equipment-status-change.controller.ts @@ -0,0 +1,59 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + Query, + Res, + UseGuards, +} from '@nestjs/common'; +import type { Response } from 'express'; +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { RolesGuard } from '../../auth/guards/roles.guard'; +import { Roles } from '../../auth/decorators/roles.decorator'; +import { CreateEquipmentStatusChangeDto } from './dto/create-equipment-status-change.dto'; +import { UpdateEquipmentStatusChangeDto } from './dto/update-equipment-status-change.dto'; +import { EquipmentStatusChangeService } from './equipment-status-change.service'; + +@Controller('change-equipment-status') +@UseGuards(JwtAuthGuard, RolesGuard) +export class EquipmentStatusChangeController { + constructor(private readonly equipmentStatusChangeService: EquipmentStatusChangeService) {} + + @Get() + @Roles('viewer', 'editor', 'admin') + list(@Query() query: Record, @Res({ passthrough: true }) response: Response) { + return this.equipmentStatusChangeService.list(query, response); + } + + @Post() + @Roles('editor', 'admin') + create(@Body() dto: CreateEquipmentStatusChangeDto) { + return this.equipmentStatusChangeService.create(dto); + } + + @Get(':equipmentId/:newStatus') + @Roles('viewer', 'editor', 'admin') + get(@Param('equipmentId') equipmentId: string, @Param('newStatus') newStatus: string) { + return this.equipmentStatusChangeService.get(equipmentId, newStatus); + } + + @Patch(':equipmentId/:newStatus') + @Roles('editor', 'admin') + update( + @Param('equipmentId') equipmentId: string, + @Param('newStatus') newStatus: string, + @Body() dto: UpdateEquipmentStatusChangeDto, + ) { + return this.equipmentStatusChangeService.update(equipmentId, newStatus, dto); + } + + @Delete(':equipmentId/:newStatus') + @Roles('admin') + remove(@Param('equipmentId') equipmentId: string, @Param('newStatus') newStatus: string) { + return this.equipmentStatusChangeService.remove(equipmentId, newStatus); + } +} diff --git a/server/src/modules/equipment-status-change/equipment-status-change.module.ts b/server/src/modules/equipment-status-change/equipment-status-change.module.ts new file mode 100644 index 0000000..89e45ca --- /dev/null +++ b/server/src/modules/equipment-status-change/equipment-status-change.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { EquipmentStatusChangeController } from './equipment-status-change.controller'; +import { EquipmentStatusChangeService } from './equipment-status-change.service'; + +@Module({ + controllers: [EquipmentStatusChangeController], + providers: [EquipmentStatusChangeService], + exports: [EquipmentStatusChangeService], +}) +export class EquipmentStatusChangeModule {} diff --git a/server/src/modules/equipment-status-change/equipment-status-change.service.ts b/server/src/modules/equipment-status-change/equipment-status-change.service.ts new file mode 100644 index 0000000..0282a6b --- /dev/null +++ b/server/src/modules/equipment-status-change/equipment-status-change.service.ts @@ -0,0 +1,163 @@ +import { Injectable } from '@nestjs/common'; +import type { Response } from 'express'; +import { PrismaService } from '../../prisma/prisma.service'; +import { setListHeaders } from '../../common/pagination'; +import { EquipmentStatus } from '../shared/equipment-status.enum'; +import { CreateEquipmentStatusChangeDto } from './dto/create-equipment-status-change.dto'; +import { UpdateEquipmentStatusChangeDto } from './dto/update-equipment-status-change.dto'; + +type StatusChangeListQuery = { + _start?: string; + _end?: string; + _sort?: string; + _order?: string; + q?: string; + equipmentId?: string; + newStatus?: string | string[]; + number?: string; + responsible?: string; +}; + +@Injectable() +export class EquipmentStatusChangeService { + constructor(private readonly prisma: PrismaService) {} + + private mapRecord(item: { + equipmentId: string; + newStatus: string; + number: string | null; + date: Date; + responsible: string | null; + }) { + return { + id: `${item.equipmentId}:${item.newStatus}`, + equipmentId: item.equipmentId, + newStatus: item.newStatus, + number: item.number, + date: item.date.toISOString(), + responsible: item.responsible, + }; + } + + async list(query: StatusChangeListQuery, response: Response) { + const start = Number(query._start ?? 0); + const end = Number(query._end ?? start + 10); + const take = Math.max(end - start, 0); + const sortField = query._sort === 'id' ? 'equipmentId' : query._sort || 'equipmentId'; + const sortOrder = query._order?.toLowerCase() === 'desc' ? 'desc' : 'asc'; + const newStatus = Array.isArray(query.newStatus) + ? { in: query.newStatus } + : query.newStatus + ? { in: [query.newStatus] } + : undefined; + + const where: any = { + AND: [ + query.equipmentId ? { equipmentId: { equals: query.equipmentId } } : undefined, + newStatus ? { newStatus } : undefined, + query.number + ? { number: { contains: query.number, mode: 'insensitive' as const } } + : undefined, + query.responsible + ? { + responsible: { + contains: query.responsible, + mode: 'insensitive' as const, + }, + } + : undefined, + query.q + ? { + OR: [ + { number: { contains: query.q, mode: 'insensitive' as const } }, + { + responsible: { + contains: query.q, + mode: 'insensitive' as const, + }, + }, + ], + } + : undefined, + ].filter(Boolean), + }; + + const [items, total] = await this.prisma.$transaction([ + this.prisma.changeEquipmentStatus.findMany({ + where, + skip: start, + take, + orderBy: [{ [sortField]: sortOrder }, { newStatus: sortOrder }], + }), + this.prisma.changeEquipmentStatus.count({ where }), + ]); + + setListHeaders(response, start, end, total); + return items.map((item) => this.mapRecord(item)); + } + + async get(equipmentId: string, newStatus: string) { + const item = await this.prisma.changeEquipmentStatus.findUniqueOrThrow({ + where: { + equipmentId_newStatus: { + equipmentId, + newStatus: newStatus as EquipmentStatus, + }, + }, + }); + + return this.mapRecord(item); + } + + async create(dto: CreateEquipmentStatusChangeDto) { + const item = await this.prisma.changeEquipmentStatus.create({ + data: { + equipmentId: dto.equipmentId, + newStatus: dto.newStatus, + number: dto.number ?? null, + date: new Date(dto.date), + responsible: dto.responsible ?? null, + }, + }); + + return this.mapRecord(item); + } + + async update(equipmentId: string, newStatus: string, dto: UpdateEquipmentStatusChangeDto) { + const { + id, + equipmentId: _equipmentId, + newStatus: _newStatus, + ...rest + } = dto as UpdateEquipmentStatusChangeDto & { id?: string }; + + const item = await this.prisma.changeEquipmentStatus.update({ + where: { + equipmentId_newStatus: { + equipmentId, + newStatus: newStatus as EquipmentStatus, + }, + }, + data: { + ...rest, + date: dto.date ? new Date(dto.date) : undefined, + }, + }); + + void id; + return this.mapRecord(item); + } + + async remove(equipmentId: string, newStatus: string) { + const item = await this.prisma.changeEquipmentStatus.delete({ + where: { + equipmentId_newStatus: { + equipmentId, + newStatus: newStatus as EquipmentStatus, + }, + }, + }); + + return this.mapRecord(item); + } +} diff --git a/server/src/modules/equipment/dto/create-equipment.dto.ts b/server/src/modules/equipment/dto/create-equipment.dto.ts new file mode 100644 index 0000000..4d14496 --- /dev/null +++ b/server/src/modules/equipment/dto/create-equipment.dto.ts @@ -0,0 +1,22 @@ +import { IsEnum, IsOptional, IsString } from 'class-validator'; +import { EquipmentStatus } from '../../shared/equipment-status.enum'; + +export class CreateEquipmentDto { + @IsString() + name!: string; + + @IsString() + serialNumber!: string; + + @IsOptional() + @IsString() + dateOfInspection?: string; + + @IsOptional() + @IsString() + commissionedAt?: string; + + @IsOptional() + @IsEnum(EquipmentStatus) + status?: EquipmentStatus; +} diff --git a/server/src/modules/equipment/dto/update-equipment.dto.ts b/server/src/modules/equipment/dto/update-equipment.dto.ts new file mode 100644 index 0000000..6474cbe --- /dev/null +++ b/server/src/modules/equipment/dto/update-equipment.dto.ts @@ -0,0 +1,24 @@ +import { IsEnum, IsOptional, IsString } from 'class-validator'; +import { EquipmentStatus } from '../../shared/equipment-status.enum'; + +export class UpdateEquipmentDto { + @IsOptional() + @IsString() + name?: string; + + @IsOptional() + @IsString() + serialNumber?: string; + + @IsOptional() + @IsString() + dateOfInspection?: string; + + @IsOptional() + @IsString() + commissionedAt?: string; + + @IsOptional() + @IsEnum(EquipmentStatus) + status?: EquipmentStatus; +} diff --git a/server/src/modules/equipment/equipment.controller.ts b/server/src/modules/equipment/equipment.controller.ts new file mode 100644 index 0000000..244e3ad --- /dev/null +++ b/server/src/modules/equipment/equipment.controller.ts @@ -0,0 +1,55 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + Query, + Res, + UseGuards, +} from '@nestjs/common'; +import type { Response } from 'express'; +import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; +import { RolesGuard } from '../../auth/guards/roles.guard'; +import { Roles } from '../../auth/decorators/roles.decorator'; +import { CreateEquipmentDto } from './dto/create-equipment.dto'; +import { UpdateEquipmentDto } from './dto/update-equipment.dto'; +import { EquipmentService } from './equipment.service'; + +@Controller('equipment') +@UseGuards(JwtAuthGuard, RolesGuard) +export class EquipmentController { + constructor(private readonly equipmentService: EquipmentService) {} + + @Get() + @Roles('viewer', 'editor', 'admin') + list(@Query() query: Record, @Res({ passthrough: true }) response: Response) { + return this.equipmentService.list(query, response); + } + + @Post() + @Roles('editor', 'admin') + create(@Body() dto: CreateEquipmentDto) { + return this.equipmentService.create(dto); + } + + @Get(':id') + @Roles('viewer', 'editor', 'admin') + get(@Param('id') id: string) { + return this.equipmentService.get(id); + } + + @Patch(':id') + @Roles('editor', 'admin') + update(@Param('id') id: string, @Body() dto: UpdateEquipmentDto) { + return this.equipmentService.update(id, dto); + } + + @Delete(':id') + @Roles('admin') + remove(@Param('id') id: string) { + return this.equipmentService.remove(id); + } +} diff --git a/server/src/modules/equipment/equipment.module.ts b/server/src/modules/equipment/equipment.module.ts new file mode 100644 index 0000000..137fdba --- /dev/null +++ b/server/src/modules/equipment/equipment.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { EquipmentController } from './equipment.controller'; +import { EquipmentService } from './equipment.service'; + +@Module({ + controllers: [EquipmentController], + providers: [EquipmentService], + exports: [EquipmentService], +}) +export class EquipmentModule {} diff --git a/server/src/modules/equipment/equipment.service.ts b/server/src/modules/equipment/equipment.service.ts new file mode 100644 index 0000000..7ba8589 --- /dev/null +++ b/server/src/modules/equipment/equipment.service.ts @@ -0,0 +1,134 @@ +import { Injectable } from '@nestjs/common'; +import type { Response } from 'express'; +import { PrismaService } from '../../prisma/prisma.service'; +import { setListHeaders } from '../../common/pagination'; +import { CreateEquipmentDto } from './dto/create-equipment.dto'; +import { UpdateEquipmentDto } from './dto/update-equipment.dto'; + +type EquipmentListQuery = { + _start?: string; + _end?: string; + _sort?: string; + _order?: string; + q?: string; + name?: string; + serialNumber?: string; + status?: string | string[]; +}; + +@Injectable() +export class EquipmentService { + constructor(private readonly prisma: PrismaService) {} + + private mapRecord(item: { + id: string; + name: string; + serialNumber: string; + dateOfInspection: Date | null; + commissionedAt: Date | null; + status: string; + }) { + return { + id: item.id, + name: item.name, + serialNumber: item.serialNumber, + dateOfInspection: item.dateOfInspection?.toISOString() ?? null, + commissionedAt: item.commissionedAt?.toISOString() ?? null, + status: item.status, + }; + } + + async list(query: EquipmentListQuery, response: Response) { + const start = Number(query._start ?? 0); + const end = Number(query._end ?? start + 10); + const take = Math.max(end - start, 0); + const sortField = query._sort === 'id' ? 'id' : query._sort || 'id'; + const sortOrder = query._order?.toLowerCase() === 'desc' ? 'desc' : 'asc'; + const statusFilter = Array.isArray(query.status) + ? { in: query.status } + : query.status + ? { in: [query.status] } + : undefined; + + const where: any = { + AND: [ + query.name + ? { name: { contains: query.name, mode: 'insensitive' as const } } + : undefined, + query.serialNumber + ? { + serialNumber: { + contains: query.serialNumber, + mode: 'insensitive' as const, + }, + } + : undefined, + statusFilter ? { status: statusFilter } : undefined, + query.q + ? { + OR: [ + { name: { contains: query.q, mode: 'insensitive' as const } }, + { + serialNumber: { + contains: query.q, + mode: 'insensitive' as const, + }, + }, + ], + } + : undefined, + ].filter(Boolean), + }; + + const [items, total] = await this.prisma.$transaction([ + this.prisma.equipment.findMany({ + where, + skip: start, + take, + orderBy: { [sortField]: sortOrder }, + }), + this.prisma.equipment.count({ where }), + ]); + + setListHeaders(response, start, end, total); + return items.map((item) => this.mapRecord(item)); + } + + async get(id: string) { + const item = await this.prisma.equipment.findUniqueOrThrow({ where: { id } }); + return this.mapRecord(item); + } + + async create(dto: CreateEquipmentDto) { + const item = await this.prisma.equipment.create({ + data: { + name: dto.name, + serialNumber: dto.serialNumber, + dateOfInspection: dto.dateOfInspection ? new Date(dto.dateOfInspection) : null, + commissionedAt: dto.commissionedAt ? new Date(dto.commissionedAt) : null, + status: dto.status ?? 'Active', + }, + }); + + return this.mapRecord(item); + } + + async update(id: string, dto: UpdateEquipmentDto) { + const { id: _id, ...rest } = dto as UpdateEquipmentDto & { id?: string }; + const item = await this.prisma.equipment.update({ + where: { id }, + data: { + ...rest, + dateOfInspection: dto.dateOfInspection ? new Date(dto.dateOfInspection) : undefined, + commissionedAt: dto.commissionedAt ? new Date(dto.commissionedAt) : undefined, + }, + }); + + return this.mapRecord(item); + } + + async remove(id: string) { + const item = await this.prisma.equipment.delete({ where: { id } }); + return this.mapRecord(item); + } +} diff --git a/server/src/modules/shared/equipment-status.enum.ts b/server/src/modules/shared/equipment-status.enum.ts new file mode 100644 index 0000000..2aeedd0 --- /dev/null +++ b/server/src/modules/shared/equipment-status.enum.ts @@ -0,0 +1,4 @@ +export enum EquipmentStatus { + Active = 'Active', + Repair = 'Repair', +} diff --git a/server/src/prisma/prisma.module.ts b/server/src/prisma/prisma.module.ts new file mode 100644 index 0000000..7207426 --- /dev/null +++ b/server/src/prisma/prisma.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from '@nestjs/common'; +import { PrismaService } from './prisma.service'; + +@Global() +@Module({ + providers: [PrismaService], + exports: [PrismaService], +}) +export class PrismaModule {} diff --git a/server/src/prisma/prisma.service.ts b/server/src/prisma/prisma.service.ts new file mode 100644 index 0000000..359f950 --- /dev/null +++ b/server/src/prisma/prisma.service.ts @@ -0,0 +1,9 @@ +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit { + async onModuleInit() { + await this.$connect(); + } +} diff --git a/server/test/app.e2e-spec.ts b/server/test/app.e2e-spec.ts new file mode 100644 index 0000000..a767839 --- /dev/null +++ b/server/test/app.e2e-spec.ts @@ -0,0 +1,29 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { App } from 'supertest/types'; +import { AppModule } from './../src/app.module'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); + + afterEach(async () => { + await app.close(); + }); +}); diff --git a/server/test/jest-e2e.json b/server/test/jest-e2e.json new file mode 100644 index 0000000..e9d912f --- /dev/null +++ b/server/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/server/tsconfig.build.json b/server/tsconfig.build.json new file mode 100644 index 0000000..64f86c6 --- /dev/null +++ b/server/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 0000000..57f9635 --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext", + "resolvePackageJsonExports": true, + "esModuleInterop": true, + "isolatedModules": true, + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "noFallthroughCasesInSwitch": true + } +} diff --git a/toir-realm.json b/toir-realm.json new file mode 100644 index 0000000..bae772d --- /dev/null +++ b/toir-realm.json @@ -0,0 +1,172 @@ +{ + "realm": "toir", + "enabled": true, + "displayName": "TOIR", + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "rememberMe": true, + "verifyEmail": false, + "roles": { + "realm": [ + { + "name": "admin", + "description": "Full administrative access" + }, + { + "name": "editor", + "description": "Can create and modify data" + }, + { + "name": "viewer", + "description": "Read-only access" + } + ] + }, + "clientScopes": [ + { + "name": "api-audience", + "description": "Adds backend audience to SPA access token", + "protocol": "openid-connect", + "attributes": { + "display.on.consent.screen": "false", + "include.in.token.scope": "false" + }, + "protocolMappers": [ + { + "name": "aud-toir-backend", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "toir-backend", + "id.token.claim": "false", + "access.token.claim": "true", + "introspection.token.claim": "true" + } + } + ] + } + ], + "clients": [ + { + "clientId": "toir-frontend", + "name": "toir-frontend", + "description": "Frontend SPA client", + "enabled": true, + "protocol": "openid-connect", + "publicClient": true, + "bearerOnly": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "fullScopeAllowed": true, + "rootUrl": "https://toir-frontend.greact.ru", + "baseUrl": "https://toir-frontend.greact.ru", + "redirectUris": [ + "https://toir-frontend.greact.ru/*", + "http://localhost:5173/*" + ], + "webOrigins": [ + "https://toir-frontend.greact.ru", + "http://localhost:5173" + ], + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "defaultClientScopes": [ + "api-audience" + ], + "optionalClientScopes": [ + "offline_access" + ], + "protocolMappers": [ + { + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "id", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "sub", + "jsonType.label": "String" + } + }, + { + "name": "preferred_username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "name": "name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String" + } + } + ] + }, + { + "clientId": "toir-backend", + "name": "toir-backend", + "description": "Backend API resource server", + "enabled": true, + "protocol": "openid-connect", + "publicClient": false, + "bearerOnly": true, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "fullScopeAllowed": false + } + ] +} diff --git a/tools/api-format-to-openapi/README.md b/tools/api-format-to-openapi/README.md new file mode 100644 index 0000000..5ce3a31 --- /dev/null +++ b/tools/api-format-to-openapi/README.md @@ -0,0 +1,78 @@ +# api-format → OpenAPI 3.0 + +Абстрактная заготовка под задачу: **из доменного описания API получить OpenAPI 3.0**, затем встроить в общий пайплайн (кнопка / CI / генератор). + +## Что внутри + +| Файл | Назначение | +|------|------------| +| `examples/api-format.example.json` | Пример **не-OpenAPI** формата: ресурсы, поля, операции, query для списка | +| `prompts/llm-system.md` | Системный промпт для LLM: «верни только JSON OpenAPI 3.0.3» | +| `convert.mjs` | CLI: режим `deterministic` (маппинг в коде) и `llm` (OpenAI API) | + +## Пошаговая демонстрация в терминале + +Чтобы **постепенно** увидеть: входной api-format → что делает конвертер → структура OpenAPI: + +```bash +cd tools/api-format-to-openapi +npm run demo +``` + +С паузой после каждого шага (нажимай Enter): + +```bash +npm run demo:pause +``` + +Результат кладётся в `demo-output/openapi.json`. + +## Детерминированный режим (без LLM) + +Подходит для **фиксированной** схемы `apiFormatVersion: "1"` как в примере. + +```bash +cd tools/api-format-to-openapi +node convert.mjs --in examples/api-format.example.json --out ../../openapi.generated.json +``` + +Или через npm: + +```bash +cd tools/api-format-to-openapi +npm run convert +``` + +## Режим LLM + +Когда реальный формат отличается или богаче — прогон через модель с промптом из `prompts/llm-system.md`. + +```bash +set OPENAI_API_KEY=sk-... +cd tools/api-format-to-openapi +node convert.mjs --mode llm --in path/to/your-api-format.json --out ../../openapi.llm.json +``` + +Переменные: + +- `OPENAI_API_KEY` — обязательно +- `OPENAI_MODEL` — по умолчанию `gpt-4o-mini` +- `OPENAI_BASE_URL` — по умолчанию `https://api.openai.com/v1` (совместимо с прокси) + +## HTTP-экспортёр для AID (NestJS) + +В `server` добавлен модуль **`AidExportModule`**: `POST /aid/export/openapi` принимает `{ "apiFormat": {...}, "mode"?: "deterministic"|"llm" }` и возвращает `{ "openapi": {...} }`. Подробности: `server/src/aid-export/README.md`. + +## Интеграция позже + +1. Заменить/расширить `examples/api-format.example.json` под ваш настоящий контракт из «алабужского» гита. +2. Либо расширить `toOpenApiDeterministic` в `convert.mjs`, либо перейти на `--mode llm` с отточенным промптом. +3. Согласовать с AID точный URL, заголовки и (при необходимости) обёртку ответа; при необходимости добавить отдельный маршрут «сырой» OpenAPI без `{ openapi: ... }`. + +## Валидация OpenAPI (опционально) + +После генерации можно проверить спеку любым валидатором, например: + +```bash +npx -y @apidevtools/swagger-cli validate ../../openapi.generated.json +``` diff --git a/tools/api-format-to-openapi/convert.mjs b/tools/api-format-to-openapi/convert.mjs new file mode 100644 index 0000000..855ade0 --- /dev/null +++ b/tools/api-format-to-openapi/convert.mjs @@ -0,0 +1,341 @@ +#!/usr/bin/env node +/** + * api-format → OpenAPI 3.0 + * + * Режимы: + * --mode deterministic — маппинг только для схемы examples/api-format.example.json (и совместимых) + * --mode llm — отправка входного JSON в OpenAI Chat Completions (нужен OPENAI_API_KEY) + * + * Примеры: + * node convert.mjs --in examples/api-format.example.json --out ../../openapi.generated.json + * node convert.mjs --mode llm --in my-api.json --out openapi.json + */ + +import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +function parseArgs(argv) { + const out = { mode: "deterministic", input: null, output: null }; + for (let i = 2; i < argv.length; i++) { + const a = argv[i]; + if (a === "--mode") out.mode = argv[++i]; + else if (a === "--in") out.input = argv[++i]; + else if (a === "--out") out.output = argv[++i]; + else if (a === "-h" || a === "--help") out.help = true; + } + return out; +} + +function usage() { + console.log(` +Usage: node convert.mjs --in --out [--mode deterministic|llm] + +Environment (llm mode): + OPENAI_API_KEY required + OPENAI_MODEL optional, default gpt-4o-mini + OPENAI_BASE_URL optional, default https://api.openai.com/v1 +`); +} + +/** @param {string} t */ +function fieldToSchema(t) { + const map = { + string: { type: "string" }, + uuid: { type: "string", format: "uuid" }, + int: { type: "integer" }, + integer: { type: "integer" }, + number: { type: "number" }, + float: { type: "number" }, + boolean: { type: "boolean" }, + date: { type: "string", format: "date" }, + datetime: { type: "string", format: "date-time" }, + }; + return map[t] || { type: "string", description: `unknown type: ${t}` }; +} + +/** + * Детерминированная конвертация для apiFormatVersion "1" с полями как в example. + * @param {any} api + */ +function toOpenApiDeterministic(api) { + if (!api || api.apiFormatVersion !== "1") { + throw new Error( + 'deterministic mode: ожидается apiFormatVersion "1". Для другого формата используйте --mode llm или расширьте маппинг в convert.mjs.', + ); + } + + const base = (api.server?.basePath || "/api").replace(/\/$/, ""); + const info = api.info || { title: "API", version: "1.0.0" }; + const paths = {}; + const schemas = {}; + + for (const res of api.resources || []) { + const name = res.name; + const seg = res.pathSegment || name.toLowerCase(); + const idParam = res.idParam || "id"; + const idType = res.idType || "uuid"; + + const props = {}; + const required = []; + for (const f of res.fields || []) { + let sch; + if (f.type === "enum" && Array.isArray(f.enumValues)) { + sch = { type: "string", enum: f.enumValues }; + } else { + sch = { ...fieldToSchema(f.type) }; + } + if (f.readOnly) sch.readOnly = true; + props[f.name] = sch; + if (f.required) required.push(f.name); + } + + schemas[name] = { + type: "object", + properties: props, + ...(required.length ? { required } : {}), + }; + + const listPath = `${base}/${seg}`; + const itemPath = `${base}/${seg}/{${idParam}}`; + const idSchema = fieldToSchema(idType); + + const listQuery = []; + const lq = res.listQuery; + if (lq?.pagination) { + for (const p of lq.pagination) { + if (p === "_start" || p === "_end") + listQuery.push({ name: p, in: "query", schema: { type: "integer" }, description: "pagination" }); + } + } + if (lq?.sort) { + for (const p of lq.sort) { + if (p === "_sort") + listQuery.push({ name: "_sort", in: "query", schema: { type: "string" }, description: "sort field" }); + if (p === "_order") + listQuery.push({ + name: "_order", + in: "query", + schema: { type: "string", enum: ["asc", "desc"] }, + description: "sort order", + }); + } + } + if (lq?.filters) { + for (const p of lq.filters) { + if (p === "q") + listQuery.push({ name: "q", in: "query", schema: { type: "string" }, description: "full-text search" }); + else { + const field = (res.fields || []).find((x) => x.name === p); + const isEnum = field?.type === "enum"; + listQuery.push({ + name: p, + in: "query", + schema: isEnum + ? { type: "array", items: { type: "string", enum: field.enumValues || [] } } + : { type: "string" }, + style: isEnum ? "form" : undefined, + explode: isEnum ? true : undefined, + description: isEnum ? "repeat param for multiple values" : undefined, + }); + } + } + } + + const ops = new Set(res.operations || []); + + if (ops.has("list")) { + paths[listPath] = paths[listPath] || {}; + paths[listPath].get = { + tags: [name], + summary: `List ${name}`, + parameters: listQuery, + responses: { + "200": { + description: "OK", + content: { + "application/json": { + schema: { + type: "object", + properties: { + data: { type: "array", items: { $ref: `#/components/schemas/${name}` } }, + total: { type: "integer" }, + }, + }, + }, + }, + }, + }, + }; + } + + if (ops.has("create")) { + paths[listPath] = paths[listPath] || {}; + paths[listPath].post = { + tags: [name], + summary: `Create ${name}`, + requestBody: { + required: true, + content: { "application/json": { schema: { $ref: `#/components/schemas/${name}` } } }, + }, + responses: { + "201": { + description: "Created", + content: { "application/json": { schema: { $ref: `#/components/schemas/${name}` } } }, + }, + "400": { description: "Bad request" }, + }, + }; + } + + if (ops.has("get")) { + paths[itemPath] = paths[itemPath] || {}; + paths[itemPath].get = { + tags: [name], + summary: `Get ${name} by ${idParam}`, + parameters: [{ name: idParam, in: "path", required: true, schema: idSchema }], + responses: { + "200": { + description: "OK", + content: { "application/json": { schema: { $ref: `#/components/schemas/${name}` } } }, + }, + "404": { description: "Not found" }, + }, + }; + } + + if (ops.has("update")) { + paths[itemPath] = paths[itemPath] || {}; + paths[itemPath].patch = { + tags: [name], + summary: `Update ${name}`, + parameters: [{ name: idParam, in: "path", required: true, schema: idSchema }], + requestBody: { + content: { "application/json": { schema: { $ref: `#/components/schemas/${name}` } } }, + }, + responses: { + "200": { + description: "OK", + content: { "application/json": { schema: { $ref: `#/components/schemas/${name}` } } }, + }, + "404": { description: "Not found" }, + }, + }; + } + + if (ops.has("delete")) { + paths[itemPath] = paths[itemPath] || {}; + paths[itemPath].delete = { + tags: [name], + summary: `Delete ${name}`, + parameters: [{ name: idParam, in: "path", required: true, schema: idSchema }], + responses: { + "204": { description: "No content" }, + "404": { description: "Not found" }, + }, + }; + } + } + + const doc = { + openapi: "3.0.3", + info: { + title: info.title, + version: info.version, + ...(info.description ? { description: info.description } : {}), + }, + servers: [{ url: base || "/" }], + paths, + components: { + schemas, + ...(api.security?.type === "bearer" || api.security?.scheme === "JWT" + ? { + securitySchemes: { + bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "JWT" }, + }, + } + : {}), + }, + }; + + if (doc.components.securitySchemes) { + doc.security = [{ bearerAuth: [] }]; + for (const method of Object.values(paths)) { + for (const op of Object.values(method)) { + if (op && typeof op === "object" && op.responses) op.security = [{ bearerAuth: [] }]; + } + } + } + + return doc; +} + +async function toOpenApiLlm(apiJson) { + const key = process.env.OPENAI_API_KEY; + if (!key) throw new Error("OPENAI_API_KEY не задан"); + + const model = process.env.OPENAI_MODEL || "gpt-4o-mini"; + const baseUrl = (process.env.OPENAI_BASE_URL || "https://api.openai.com/v1").replace(/\/$/, ""); + const systemPath = resolve(__dirname, "prompts", "llm-system.md"); + const system = readFileSync(systemPath, "utf8"); + const user = JSON.stringify(apiJson, null, 2); + + const res = await fetch(`${baseUrl}/chat/completions`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${key}`, + }, + body: JSON.stringify({ + model, + temperature: 0.1, + messages: [ + { role: "system", content: system }, + { role: "user", content: `Преобразуй следующий api-format в OpenAPI 3.0.3 JSON:\n\n${user}` }, + ], + }), + }); + + if (!res.ok) { + const text = await res.text(); + throw new Error(`OpenAI HTTP ${res.status}: ${text}`); + } + + const data = await res.json(); + const content = data.choices?.[0]?.message?.content; + if (!content) throw new Error("Пустой ответ от модели"); + + const trimmed = content.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, ""); + return JSON.parse(trimmed); +} + +async function main() { + const args = parseArgs(process.argv); + if (args.help || !args.input || !args.output) { + usage(); + process.exit(args.help ? 0 : 1); + } + + const inputPath = resolve(process.cwd(), args.input); + const outputPath = resolve(process.cwd(), args.output); + const raw = readFileSync(inputPath, "utf8"); + const api = JSON.parse(raw); + + let openapi; + if (args.mode === "llm") { + openapi = await toOpenApiLlm(api); + } else { + openapi = toOpenApiDeterministic(api); + } + + mkdirSync(dirname(outputPath), { recursive: true }); + writeFileSync(outputPath, JSON.stringify(openapi, null, 2), "utf8"); + console.log(`Written: ${outputPath}`); +} + +main().catch((e) => { + console.error(e.message || e); + process.exit(1); +}); diff --git a/tools/api-format-to-openapi/demo-steps.mjs b/tools/api-format-to-openapi/demo-steps.mjs new file mode 100644 index 0000000..38865fe --- /dev/null +++ b/tools/api-format-to-openapi/demo-steps.mjs @@ -0,0 +1,117 @@ +#!/usr/bin/env node +/** + * Пошаговая демонстрация: api-format → OpenAPI 3.0 (детерминированный режим). + * + * node demo-steps.mjs — все шаги подряд в консоли + * node demo-steps.mjs --pause — пауза после каждого шага (Enter) + * + * Результат также пишется в demo-output/openapi.json рядом со скриптом. + */ + +import { execFileSync } from "node:child_process"; +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { createInterface } from "node:readline/promises"; +import { stdin as input, stdout as output } from "node:process"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const EXAMPLE = join(__dirname, "examples", "api-format.example.json"); +const OUT_DIR = join(__dirname, "demo-output"); +const OUT_OPENAPI = join(OUT_DIR, "openapi.json"); +const usePause = process.argv.includes("--pause"); + +function banner(title) { + const line = "═".repeat(Math.min(60, title.length + 8)); + console.log(`\n${line}\n ${title}\n${line}\n`); +} + +async function pause(msg = "Нажми Enter, чтобы перейти к следующему шагу…") { + if (!usePause) return; + const rl = createInterface({ input, output }); + await rl.question(msg); + rl.close(); +} + +async function main() { + console.clear?.(); + + banner("Шаг 0. Задача"); + console.log( + "У нас есть описание API в СВОЁМ формате (api-format), не OpenAPI.\n" + + "Нужно получить стандартную спецификацию OpenAPI 3.0 — для Swagger, клиентов, AID.\n" + + "Сейчас покажем путь на учебном примере (детерминированный маппинг в convert.mjs).", + ); + await pause(); + + banner("Шаг 1. Входной файл (фрагмент api-format)"); + console.log(`Файл: ${EXAMPLE}\n`); + const rawIn = readFileSync(EXAMPLE, "utf8"); + const apiFormat = JSON.parse(rawIn); + console.log(JSON.stringify(apiFormat, null, 2)); + console.log( + "\n↑ Это НЕ OpenAPI. Здесь: версия формата, info, basePath, ресурс Equipment с полями и операциями CRUD.", + ); + await pause(); + + banner("Шаг 2. Что делает конвертер (логика)"); + console.log(` + • apiFormatVersion "1" → включается ветка toOpenApiDeterministic в convert.mjs + • Ресурс Equipment → components.schemas.Equipment + пути /api/equipment и /api/equipment/{id} + • Поля (string, uuid, enum…) → JSON Schema в components.schemas + • listQuery → query-параметры (_start, _end, _sort, _order, q, status…) + • security bearer → components.securitySchemes + security на операциях +`); + await pause(); + + banner("Шаг 3. Запуск convert.mjs"); + mkdirSync(OUT_DIR, { recursive: true }); + const convertScript = join(__dirname, "convert.mjs"); + console.log(`Команда:\n node convert.mjs --in examples/api-format.example.json --out demo-output/openapi.json\n`); + execFileSync(process.execPath, [convertScript, "--in", EXAMPLE, "--out", OUT_OPENAPI], { + stdio: "inherit", + }); + console.log(`\nГотово. Файл: ${OUT_OPENAPI}`); + await pause(); + + banner("Шаг 4. Результат — структура OpenAPI"); + const spec = JSON.parse(readFileSync(OUT_OPENAPI, "utf8")); + console.log(`openapi: ${spec.openapi}`); + console.log(`title: ${spec.info?.title}`); + console.log(`version: ${spec.info?.version}`); + console.log("\nПути (paths):"); + for (const p of Object.keys(spec.paths || {}).sort()) { + const methods = Object.keys(spec.paths[p]).join(", "); + console.log(` ${p} [${methods}]`); + } + console.log("\nСхемы (components.schemas):", Object.keys(spec.components?.schemas || {}).join(", ")); + await pause(); + + banner("Шаг 5. Фрагмент: GET список (одна операция)"); + const listPath = Object.keys(spec.paths || {}).find((k) => k.endsWith("/equipment") && !k.includes("{")); + if (listPath && spec.paths[listPath]?.get) { + console.log(JSON.stringify({ [listPath]: { get: spec.paths[listPath].get } }, null, 2)); + } else { + console.log("(путь списка не найден — открой demo-output/openapi.json)"); + } + await pause(); + + banner("Шаг 6. Как проверить дальше"); + console.log(` + 1) Открой целиком: ${OUT_OPENAPI} + 2) Валидация (из корня репозитория): + npx -y @apidevtools/swagger-cli validate tools/api-format-to-openapi/demo-output/openapi.json + 3) Через Nest (сервер на 3001): + POST http://127.0.0.1:3001/aid/export/openapi + тело: { "apiFormat": <содержимое api-format.example.json>, "mode": "deterministic" } + 4) Режим LLM (другой входной JSON): + node convert.mjs --mode llm --in your.json --out openapi.llm.json + (нужен OPENAI_API_KEY) +`); + console.log("Демо завершено.\n"); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/tools/api-format-to-openapi/examples/api-format.example.json b/tools/api-format-to-openapi/examples/api-format.example.json new file mode 100644 index 0000000..056882c --- /dev/null +++ b/tools/api-format-to-openapi/examples/api-format.example.json @@ -0,0 +1,36 @@ +{ + "apiFormatVersion": "1", + "info": { + "title": "TOiR Demo API", + "version": "1.0.0", + "description": "Абстрактный пример доменного описания API (не OpenAPI)." + }, + "server": { + "basePath": "/api" + }, + "security": { + "type": "bearer", + "scheme": "JWT" + }, + "resources": [ + { + "name": "Equipment", + "pathSegment": "equipment", + "idParam": "id", + "idType": "uuid", + "fields": [ + { "name": "id", "type": "uuid", "readOnly": true }, + { "name": "inventoryNumber", "type": "string", "required": true }, + { "name": "name", "type": "string", "required": true }, + { "name": "status", "type": "enum", "enumValues": ["Active", "Repair", "Decommissioned"] }, + { "name": "location", "type": "string" } + ], + "operations": ["list", "get", "create", "update", "delete"], + "listQuery": { + "pagination": ["_start", "_end"], + "sort": ["_sort", "_order"], + "filters": ["q", "status"] + } + } + ] +} diff --git a/tools/api-format-to-openapi/package.json b/tools/api-format-to-openapi/package.json new file mode 100644 index 0000000..e457ff0 --- /dev/null +++ b/tools/api-format-to-openapi/package.json @@ -0,0 +1,12 @@ +{ + "name": "api-format-to-openapi", + "private": true, + "type": "module", + "description": "Конвертация доменного api-format в OpenAPI 3.0 (детерминированно или через LLM)", + "scripts": { + "convert": "node convert.mjs --in examples/api-format.example.json --out ../../openapi.generated.json", + "convert:llm": "node convert.mjs --mode llm --in examples/api-format.example.json --out ../../openapi.llm.json", + "demo": "node demo-steps.mjs", + "demo:pause": "node demo-steps.mjs --pause" + } +} diff --git a/tools/api-format-to-openapi/prompts/llm-system.md b/tools/api-format-to-openapi/prompts/llm-system.md new file mode 100644 index 0000000..4b17758 --- /dev/null +++ b/tools/api-format-to-openapi/prompts/llm-system.md @@ -0,0 +1,30 @@ +# Роль + +Ты конвертер доменного описания API в спецификацию **OpenAPI 3.0.3** (JSON). + +# Вход + +Пользователь пришлёт один JSON-файл в произвольном «доменном» формате (api-format). В нём могут быть сущности, поля, типы, пути, операции, фильтры, авторизация. + +# Выход + +- Верни **только** валидный JSON объекта OpenAPI 3.0.3. +- Без markdown, без комментариев, без текста до или после JSON. +- Используй `openapi: "3.0.3"`. +- Опиши `info`, `servers`, при необходимости `tags`. +- Для каждой сущности/ресурса создай `components.schemas` и `paths` с типичными REST-операциями, если они указаны. +- Типы полей маппь так: + - `string` → `type: string` + - `uuid` → `type: string`, `format: uuid` + - `int` / `integer` → `type: integer` + - `number` / `float` → `type: number` + - `boolean` → `type: boolean` + - `date` / `datetime` → `type: string`, `format: date` или `date-time` + - `enum` + список значений → `type: string`, `enum: [...]` +- Для списков с пагинацией добавь query-параметры из входа (`_start`, `_end`, `_sort`, `_order`, фильтры). +- Для `401/403/404/500` добавь минимальные `responses` с `description`. +- Если во входе указана Bearer/JWT — добавь `components.securitySchemes` и `security` на путях или глобально. + +# Если чего-то не хватает + +Делай разумные допущения и кратко отражай их в `info.description` одним предложением. diff --git a/tools/api-summary-to-openapi.mjs b/tools/api-summary-to-openapi.mjs new file mode 100644 index 0000000..278e2f8 --- /dev/null +++ b/tools/api-summary-to-openapi.mjs @@ -0,0 +1,301 @@ +// Deterministic OpenAPI 3.0.3 generator from api-summary.json / toir.api.dsl. +// +// This script is part of the Tier 1 deterministic preprocessing layer. +// It converts the canonical api-summary (produced by tools/api-summary.mjs) +// into a valid OpenAPI 3.0.3 document. +// +// Usage: +// node tools/api-summary-to-openapi.mjs --out openapi.json +// npm run generate:openapi +// +// No LLM involvement. The output is reproducible from DSL + this script alone. + +import { writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { buildApiSummary } from './api-summary.mjs'; + +const rootDir = process.cwd(); + +// --------------------------------------------------------------------------- +// DSL scalar → OpenAPI type +// --------------------------------------------------------------------------- + +function dslTypeToOpenApi(dslType) { + switch (dslType) { + case 'uuid': + return { type: 'string', format: 'uuid' }; + case 'string': + return { type: 'string' }; + case 'text': + return { type: 'string' }; + case 'integer': + return { type: 'integer', format: 'int32' }; + case 'number': + return { type: 'number' }; + case 'decimal': + return { type: 'string', format: 'decimal' }; + case 'date': + return { type: 'string', format: 'date-time' }; + case 'boolean': + return { type: 'boolean' }; + default: + // enum names and DTO references handled by caller + return null; + } +} + +// --------------------------------------------------------------------------- +// Resolve a DSL field type to an OpenAPI schema reference or inline schema. +// dtoNames — Set of known DTO names in the api summary. +// enumNames — Set of known enum names (derived from type mappings table). +// --------------------------------------------------------------------------- + +function resolveFieldType(dslType, dtoNames, enumNames) { + if (!dslType) return { type: 'object' }; + + // Array type: "DTO.Foo[]" + if (dslType.endsWith('[]')) { + const inner = dslType.slice(0, -2); + return { type: 'array', items: resolveFieldType(inner, dtoNames, enumNames) }; + } + + // Scalar + const scalar = dslTypeToOpenApi(dslType); + if (scalar) return scalar; + + // DTO reference + if (dtoNames.has(dslType)) { + return { $ref: `#/components/schemas/${dslType.replace(/^DTO\./, '')}` }; + } + + // Enum reference + if (enumNames.has(dslType)) { + return { $ref: `#/components/schemas/${dslType}` }; + } + + // Unknown — emit as string with x-dsl-type annotation + return { type: 'string', 'x-dsl-type': dslType }; +} + +// --------------------------------------------------------------------------- +// Build OpenAPI schema object from a DTO definition +// --------------------------------------------------------------------------- + +function buildDtoSchema(dto, dtoNames, enumNames) { + const properties = {}; + const required = []; + + for (const field of dto.fields) { + const schema = resolveFieldType(field.type, dtoNames, enumNames); + if (field.description && schema.$ref) { + // OpenAPI 3.0.3: $ref cannot have sibling keys — wrap with allOf + properties[field.name] = { allOf: [schema], description: field.description }; + } else { + if (field.description) schema.description = field.description; + properties[field.name] = schema; + } + if (field.required) required.push(field.name); + } + + const schema = { + type: 'object', + properties, + }; + + if (dto.description) schema.description = dto.description; + if (required.length > 0) schema.required = required; + + return schema; +} + +// --------------------------------------------------------------------------- +// Convert DSL HTTP method to OpenAPI method key +// --------------------------------------------------------------------------- + +function methodKey(method) { + return (method ?? 'get').toLowerCase(); +} + +// --------------------------------------------------------------------------- +// Build OpenAPI path item for an endpoint +// --------------------------------------------------------------------------- + +function buildPathOperation(endpoint, apiDescription, dtoNames, enumNames) { + const operation = {}; + + if (endpoint.description) operation.summary = endpoint.description; + + // Security — all endpoints require bearer auth + operation.security = [{ bearerAuth: [] }]; + + // Tags — derive from API name or path + const tag = apiDescription ? apiDescription.replace(/^API управления\s+/i, '').replace(/ами$/, '') : undefined; + if (tag) operation.tags = [tag]; + + // Parameters — detect path params by matching attribute names against {param} in the path + const pathTemplate = endpoint.path ?? ''; + const pathParamNames = new Set( + [...pathTemplate.matchAll(/\{(\w+)\}/g)].map((m) => m[1]), + ); + const pathParams = endpoint.attributes.filter((a) => pathParamNames.has(a.name)); + if (pathParams.length > 0) { + operation.parameters = pathParams.map((p) => ({ + name: p.name, + in: 'path', + required: true, + schema: resolveFieldType(p.type, dtoNames, enumNames), + ...(p.description ? { description: p.description } : {}), + })); + } + + // Request body + const requestAttr = endpoint.attributes.find((a) => a.name === 'request'); + if (requestAttr) { + operation.requestBody = { + required: true, + content: { + 'application/json': { + schema: resolveFieldType(requestAttr.type, dtoNames, enumNames), + }, + }, + }; + } + + // Response + const responseAttr = endpoint.attributes.find((a) => a.name === 'response'); + const responseSchema = responseAttr + ? resolveFieldType(responseAttr.type, dtoNames, enumNames) + : { type: 'object' }; + + const successCode = endpoint.method === 'POST' && !endpoint.path?.endsWith('/page') ? '201' : '200'; + + operation.responses = { + [successCode]: { + description: 'Success', + content: { + 'application/json': { + schema: responseSchema, + }, + }, + }, + '401': { description: 'Unauthorized' }, + '403': { description: 'Forbidden' }, + }; + + if (endpoint.method === 'DELETE') { + operation.responses = { + '204': { description: 'No content' }, + '401': { description: 'Unauthorized' }, + '403': { description: 'Forbidden' }, + '404': { description: 'Not found' }, + }; + delete operation.responses['201']; + } + + return operation; +} + +// --------------------------------------------------------------------------- +// Main builder +// --------------------------------------------------------------------------- + +function buildOpenApi(summary) { + const dtoNames = new Set(summary.dtos.map((d) => d.name)); + + // Build enum map from api-summary enums block (fully declared enums with values) + const enumMap = new Map((summary.enums ?? []).map((e) => [e.name, e])); + + // Also collect enum names referenced in DTO fields that are not in the declared enums + // (covers cases where enums are declared in domain.dsl but referenced in api.dsl) + const enumNames = new Set(enumMap.keys()); + for (const dto of summary.dtos) { + for (const field of dto.fields) { + const t = field.type?.replace('[]', ''); + if (t && !dtoNames.has(t) && !dslTypeToOpenApi(t)) { + enumNames.add(t); + } + } + } + + // Schemas — one per DTO + const schemas = {}; + for (const dto of summary.dtos) { + const schemaName = dto.name.replace(/^DTO\./, ''); + schemas[schemaName] = buildDtoSchema(dto, dtoNames, enumNames); + } + + // Enum schemas — use actual values when available, otherwise annotate as opaque string enum + for (const enumName of enumNames) { + const enumDef = enumMap.get(enumName); + if (enumDef && enumDef.values.length > 0) { + schemas[enumName] = { + type: 'string', + enum: enumDef.values.map((v) => v.name), + 'x-enum-labels': Object.fromEntries( + enumDef.values.filter((v) => v.label).map((v) => [v.name, v.label]), + ), + ...(enumDef.description ? { description: enumDef.description } : {}), + }; + } else { + schemas[enumName] = { + type: 'string', + 'x-dsl-enum': enumName, + description: `Enum: ${enumName} (values defined in domain/*.api.dsl)`, + }; + } + } + + // Paths + const paths = {}; + for (const api of summary.apis) { + for (const endpoint of api.endpoints) { + if (!endpoint.path) continue; + const pathKey = endpoint.path; + if (!paths[pathKey]) paths[pathKey] = {}; + const opKey = methodKey(endpoint.method); + paths[pathKey][opKey] = buildPathOperation(endpoint, api.description, dtoNames, enumNames); + } + } + + return { + openapi: '3.0.3', + info: { + title: 'KIS-TOiR API', + description: + 'Equipment maintenance management system. Generated from domain/toir.api.dsl via tools/api-summary-to-openapi.mjs.', + version: '1.0.0', + }, + servers: [ + { + url: '/api', + description: 'Default server', + }, + ], + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + }, + }, + schemas, + }, + paths, + }; +} + +// --------------------------------------------------------------------------- +// CLI +// --------------------------------------------------------------------------- + +const args = process.argv.slice(2); +const outIndex = args.indexOf('--out'); +const outPath = outIndex !== -1 ? args[outIndex + 1] : 'openapi.json'; + +const summary = buildApiSummary(rootDir); +const openApiDoc = buildOpenApi(summary); +const outputPath = path.resolve(rootDir, outPath); + +writeFileSync(outputPath, `${JSON.stringify(openApiDoc, null, 2)}\n`, 'utf8'); +console.log(`Generated ${path.relative(rootDir, outputPath)}`); diff --git a/tools/api-summary.mjs b/tools/api-summary.mjs new file mode 100644 index 0000000..b5e2279 --- /dev/null +++ b/tools/api-summary.mjs @@ -0,0 +1,389 @@ +import { readdirSync, readFileSync } from 'node:fs'; +import path from 'node:path'; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function stripInlineComment(line) { + let inString = false; + let result = ''; + + for (let index = 0; index < line.length; index += 1) { + const current = line[index]; + const next = line[index + 1]; + + if (current === '"' && line[index - 1] !== '\\') { + inString = !inString; + result += current; + continue; + } + + if (!inString && current === '/' && next === '/') { + break; + } + + result += current; + } + + return result.trim(); +} + +// --------------------------------------------------------------------------- +// File discovery +// --------------------------------------------------------------------------- + +export function getApiDslFiles(rootDir) { + const domainDir = path.join(rootDir, 'domain'); + try { + return readdirSync(domainDir, { withFileTypes: true }) + .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.api.dsl')) + .map((entry) => path.join(domainDir, entry.name)) + .sort((left, right) => left.localeCompare(right)); + } catch { + return []; + } +} + +// --------------------------------------------------------------------------- +// Parser +// +// Parses all *.api.dsl files using a stack-based approach. +// This is the single canonical parser for the API DSL. +// +// Supported top-level blocks: +// enum { description?; value { label?; } } +// dto DTO. { description?; attribute { ... } } +// api API. { description?; endpoint { ... } } +// +// DTO attribute modifiers (any order inside the attribute block): +// type ; +// description "..."; +// map Entity.field; +// sync Entity.field; (alias for map — used for computed/aggregate fields) +// is required; +// is nullable; +// is unique; +// key primary; +// label "..."; +// +// Endpoint modifiers: +// label "METHOD /path"; +// description "..."; +// attribute { type ; description?; } +// +// Returns: +// { +// files: string[], +// enums: EnumBlock[], +// dtos: DtoBlock[], +// apis: ApiBlock[], +// } +// +// EnumBlock = { name, description, values: EnumValue[] } +// EnumValue = { name, label } +// DtoBlock = { name, description, fields: DtoField[] } +// DtoField = { name, type, required, nullable, unique, primary, description, map, label } +// ApiBlock = { name, description, endpoints: Endpoint[] } +// Endpoint = { name, label, method, path, description, attributes: EndpointAttr[] } +// EndpointAttr = { name, type, description } +// --------------------------------------------------------------------------- + +export function parseApiDsl(rootDir) { + const files = getApiDslFiles(rootDir); + const enums = []; + const dtos = []; + const apis = []; + const stack = []; + + for (const filePath of files) { + const content = readFileSync(filePath, 'utf8'); + const lines = content.split(/\r?\n/); + + for (const rawLine of lines) { + const line = stripInlineComment(rawLine); + if (!line) continue; + + const top = stack.at(-1); + + // ── Top-level: enum block ────────────────────────────────────────── + const enumMatch = line.match(/^enum\s+([A-Za-z][A-Za-z0-9_]*)\s*\{$/); + if (!top && enumMatch) { + const enumBlock = { name: enumMatch[1], description: null, values: [] }; + enums.push(enumBlock); + stack.push({ type: 'enum', ref: enumBlock }); + continue; + } + + // ── Top-level: dto block ─────────────────────────────────────────── + const dtoMatch = line.match(/^dto\s+(DTO\.\w+)\s*\{$/); + if (!top && dtoMatch) { + const dto = { name: dtoMatch[1], description: null, fields: [] }; + dtos.push(dto); + stack.push({ type: 'dto', ref: dto }); + continue; + } + + // ── Top-level: api block ─────────────────────────────────────────── + const apiMatch = line.match(/^api\s+(API\.\w+)\s*\{$/); + if (!top && apiMatch) { + const api = { name: apiMatch[1], description: null, endpoints: [] }; + apis.push(api); + stack.push({ type: 'api', ref: api }); + continue; + } + + // ── Inside enum ─────────────────────────────────────────────────── + if (top?.type === 'enum') { + const descMatch = line.match(/^description\s+"(.*)"\s*;$/); + if (descMatch) { + top.ref.description = descMatch[1]; + continue; + } + + const valueMatch = line.match(/^value\s+([^\s{]+)\s*\{$/); + if (valueMatch) { + const enumValue = { name: valueMatch[1], label: null }; + top.ref.values.push(enumValue); + stack.push({ type: 'enumValue', ref: enumValue }); + continue; + } + } + + // ── Inside enum value ───────────────────────────────────────────── + if (top?.type === 'enumValue') { + const labelMatch = line.match(/^label\s+"(.*)"\s*;$/); + if (labelMatch) { + top.ref.label = labelMatch[1]; + continue; + } + } + + // ── Inside dto ──────────────────────────────────────────────────── + if (top?.type === 'dto') { + const descMatch = line.match(/^description\s+"(.*)"\s*;$/); + if (descMatch) { + top.ref.description = descMatch[1]; + continue; + } + + const attrMatch = line.match(/^attribute\s+(\w+)\s*\{$/); + if (attrMatch) { + const field = { + name: attrMatch[1], + type: null, + required: false, + nullable: false, + unique: false, + primary: false, + description: null, + map: null, + label: null, + }; + top.ref.fields.push(field); + stack.push({ type: 'dtoField', ref: field }); + continue; + } + } + + // ── Inside dto attribute field ──────────────────────────────────── + if (top?.type === 'dtoField') { + const typeMatch = line.match(/^type\s+(.+?)\s*;$/); + if (typeMatch) { + top.ref.type = typeMatch[1]; + continue; + } + + if (/^is\s+required\s*;$/.test(line)) { + top.ref.required = true; + continue; + } + + if (/^is\s+nullable\s*;$/.test(line)) { + top.ref.nullable = true; + continue; + } + + if (/^is\s+unique\s*;$/.test(line)) { + top.ref.unique = true; + continue; + } + + if (/^key\s+primary\s*;$/.test(line)) { + top.ref.primary = true; + continue; + } + + const descMatch = line.match(/^description\s+"(.*)"\s*;$/); + if (descMatch) { + top.ref.description = descMatch[1]; + continue; + } + + // map Entity.field; — canonical field mapping + const mapMatch = line.match(/^map\s+(\w+)\.(\w+)\s*;$/); + if (mapMatch) { + top.ref.map = `${mapMatch[1]}.${mapMatch[2]}`; + continue; + } + + // sync Entity.field; — aggregate / computed field mapping (treated as map) + const syncMatch = line.match(/^sync\s+(\w+)\.(\w+)\s*;$/); + if (syncMatch) { + top.ref.map = `${syncMatch[1]}.${syncMatch[2]}`; + top.ref.sync = true; + continue; + } + + const labelMatch = line.match(/^label\s+"(.*)"\s*;$/); + if (labelMatch) { + top.ref.label = labelMatch[1]; + continue; + } + } + + // ── Inside api ──────────────────────────────────────────────────── + if (top?.type === 'api') { + const descMatch = line.match(/^description\s+"(.*)"\s*;$/); + if (descMatch) { + top.ref.description = descMatch[1]; + continue; + } + + const epMatch = line.match(/^endpoint\s+(\w+)\s*\{$/); + if (epMatch) { + const ep = { + name: epMatch[1], + label: null, + method: null, + path: null, + description: null, + attributes: [], + }; + top.ref.endpoints.push(ep); + stack.push({ type: 'endpoint', ref: ep }); + continue; + } + } + + // ── Inside endpoint ─────────────────────────────────────────────── + if (top?.type === 'endpoint') { + const labelMatch = line.match(/^label\s+"([^"]+)"\s*;$/); + if (labelMatch) { + top.ref.label = labelMatch[1]; + const parts = labelMatch[1].split(' ', 2); + top.ref.method = parts[0]?.toUpperCase() ?? null; + top.ref.path = parts[1] ?? null; + continue; + } + + const descMatch = line.match(/^description\s+"(.*)"\s*;$/); + if (descMatch) { + top.ref.description = descMatch[1]; + continue; + } + + const attrMatch = line.match(/^attribute\s+(\w+)\s*\{$/); + if (attrMatch) { + const attr = { name: attrMatch[1], type: null, description: null }; + top.ref.attributes.push(attr); + stack.push({ type: 'endpointAttr', ref: attr }); + continue; + } + } + + // ── Inside endpoint attribute ───────────────────────────────────── + if (top?.type === 'endpointAttr') { + const typeMatch = line.match(/^type\s+(.+?)\s*;$/); + if (typeMatch) { + top.ref.type = typeMatch[1]; + continue; + } + + const descMatch = line.match(/^description\s+"(.*)"\s*;$/); + if (descMatch) { + top.ref.description = descMatch[1]; + continue; + } + } + + // ── Closing brace — pop the stack ───────────────────────────────── + if (/^}\s*;?$/.test(line)) { + stack.pop(); + } + } + } + + return { files, enums, dtos, apis }; +} + +// --------------------------------------------------------------------------- +// Summary builder +// +// Produces the serialisable api-summary.json object. +// --------------------------------------------------------------------------- + +export function buildApiSummary(rootDir) { + const { files, enums, dtos, apis } = parseApiDsl(rootDir); + + // Detect duplicate DTO names + const dtoNames = new Set(); + for (const dto of dtos) { + if (dtoNames.has(dto.name)) { + throw new Error(`Duplicate DTO definition: ${dto.name}`); + } + dtoNames.add(dto.name); + } + + // Detect duplicate API names + const apiNames = new Set(); + for (const api of apis) { + if (apiNames.has(api.name)) { + throw new Error(`Duplicate API definition: ${api.name}`); + } + apiNames.add(api.name); + } + + return { + sourceFiles: files.map((filePath) => + path.relative(rootDir, filePath).replaceAll('\\', '/'), + ), + enums: enums.map((e) => ({ + name: e.name, + description: e.description, + values: e.values.map((v) => ({ name: v.name, label: v.label })), + })), + dtos: dtos.map((dto) => ({ + name: dto.name, + description: dto.description, + fields: dto.fields.map((field) => ({ + name: field.name, + type: field.type, + required: field.required, + nullable: field.nullable, + unique: field.unique, + primary: field.primary, + description: field.description, + map: field.map, + sync: field.sync ?? false, + label: field.label, + })), + })), + apis: apis.map((api) => ({ + name: api.name, + description: api.description, + endpoints: api.endpoints.map((ep) => ({ + name: ep.name, + label: ep.label, + method: ep.method, + path: ep.path, + description: ep.description, + attributes: ep.attributes.map((attr) => ({ + name: attr.name, + type: attr.type, + description: attr.description, + })), + })), + })), + }; +} diff --git a/tools/eval/README.md b/tools/eval/README.md new file mode 100644 index 0000000..7d62337 --- /dev/null +++ b/tools/eval/README.md @@ -0,0 +1,113 @@ +# Eval Harness — Rule 6 + +Fixture-based regression tests for generated artifacts. + +## Why this exists + +> "Evals are the test suite for your prompts. You would never ship code without tests; +> don't ship prompts without evals." — Anthropic Engineering + +The validation gate (`tools/validate-generation.mjs`) checks **existence** and **structural compliance**. +The eval harness checks **semantic correctness**: are the right patterns present in the generated code? +Do the generated files actually follow the rules in `prompts/`? + +Together they enforce: +- Gate: "file exists, field names present, auth seams wired" +- Evals: "DTO has class-validator decorators, FK uses ReferenceInput, date uses DateInput, guard is present" + +The committed fixture corpus is a reviewed semantic contract. It may be scaffolded from source-of-truth as a helper, but it should not be auto-regenerated wholesale during every full regeneration run, or it stops acting as an independent regression signal. + +## Usage + +```bash +# Run all evals +npm run eval:generation + +# Run evals for one entity +node tools/eval/run-evals.mjs --entity equipment + +# Verbose output (show each file being checked) +node tools/eval/run-evals.mjs --verbose +``` + +## Fixture format + +Each fixture lives in `tools/eval/fixtures//`: + +``` +fixtures/ + equipment/ + meta.json ← what this fixture tests + backend.assertions.json ← patterns the NestJS files must satisfy + frontend.assertions.json ← patterns the React Admin files must satisfy + repair-order/ + meta.json + backend.assertions.json + frontend.assertions.json +``` + +### `meta.json` + +```json +{ + "entity": "Equipment", + "kebab": "equipment", + "resource": "equipment", + "description": "...", + "tests": ["dto-decorator-coverage", "auth-guards", ...] +} +``` + +### `*.assertions.json` + +Each file entry supports: + +| Key | Type | Meaning | +|-----|------|---------| +| `path` | string | Relative path from repo root | +| `must_contain` | string[] | Each string must appear as a literal substring | +| `must_not_contain` | string[] | Each string must NOT appear | +| `must_match_regex` | string[] | Each pattern must match (multiline dot-all) | +| `must_not_match_regex` | string[] | Each pattern must NOT match | +| `comment` | string | Human-readable explanation of what is being tested | + +## Eval-driven development workflow + +This is the critical principle from Anthropic and Google: + +1. **Write the failing eval first.** When you change a prompt or add a rule, add an + assertion that captures the new expectation *before* re-generating. +2. **Run evals**: `npm run eval:generation` → see failures. +3. **Re-generate** the affected entity (following the generation workflow in `AGENTS.md`). +4. **Run evals again**: all pass → the change is verified. +5. **Commit both** the updated fixture and the regenerated artifacts together. + +A passing eval after a prompt change confirms the LLM followed the new rule. +A failing eval before a prompt change tells you exactly which prior contract was broken. + +Automation note: + +- It is reasonable to generate starter fixtures or coverage manifests from source-of-truth. +- It is not reasonable to let the same regeneration step auto-refresh the authoritative committed eval corpus, because that couples the semantic gate too tightly to the generator and can hide regressions. + +## Adding a new entity fixture + +When adding a new entity to `domain/toir.api.dsl` and generating its backend + frontend: + +1. Create `tools/eval/fixtures//meta.json` +2. Create `tools/eval/fixtures//backend.assertions.json` with at minimum: + - controller: `@Controller(...)`, `@UseGuards(`, `JwtAuthGuard`, HTTP methods + - create_dto: `from 'class-validator'`, required fields with `!:`, `@IsString(`, `@IsOptional(` + - update_dto: `from 'class-validator'`, fields with `?:`, `@IsOptional(` +3. Create `tools/eval/fixtures//frontend.assertions.json` with at minimum: + - create: `ReferenceInput` for FK fields, `NumberInput` for numeric, `DateInput` for date, `SelectInput` for enum + - show: `ReferenceField` for FK fields, `DateField` for date +4. Run `npm run eval:generation` to verify the fixture catches real issues. + +## Integration with git hooks + +The pre-commit hook (installed by `npm run install-hooks`) runs both: +1. `node tools/validate-generation.mjs --artifacts-only` — existence gate +2. `npm run eval:generation` — semantic eval gate + +Both must pass before a commit is accepted. diff --git a/tools/eval/fixtures/change-equipment-status/backend.assertions.json b/tools/eval/fixtures/change-equipment-status/backend.assertions.json new file mode 100644 index 0000000..72144fd --- /dev/null +++ b/tools/eval/fixtures/change-equipment-status/backend.assertions.json @@ -0,0 +1,72 @@ +{ + "entity": "ChangeEquipmentStatus", + "files": { + "controller": { + "path": "server/src/modules/change-equipment-status/change-equipment-status.controller.ts", + "must_contain": [ + "@Controller('change-equipment-status')", + "@UseGuards(", + "JwtAuthGuard", + "RolesGuard", + "@Get()", + "@Post()", + "@Get(':equipmentId/:newStatus')", + "@Patch(':equipmentId/:newStatus')", + "@Delete(':equipmentId/:newStatus')" + ], + "must_not_contain": [ + "@Put(':equipmentId/:newStatus')" + ], + "must_match_regex": [ + "@Delete\\(':equipmentId/:newStatus'\\)[\\s\\S]{0,120}@Roles\\('admin'\\)|@Roles\\('admin'\\)[\\s\\S]{0,120}@Delete\\(':equipmentId/:newStatus'\\)" + ] + }, + "service": { + "path": "server/src/modules/change-equipment-status/change-equipment-status.service.ts", + "must_contain": [ + "setListHeaders", + "_start", + "_end", + "_sort", + "_order", + "equipmentId", + "newStatus" + ], + "must_match_regex": [ + "equipmentId.*(equals|=)", + "newStatus.*in\\b|\\bin\\b.*newStatus" + ] + }, + "create_dto": { + "path": "server/src/modules/change-equipment-status/dto/create-change-equipment-status.dto.ts", + "must_contain": [ + "from 'class-validator'", + "equipmentId!:", + "newStatus!:", + "date!:", + "number?:", + "responsible?:", + "@IsUUID(", + "@IsEnum(", + "@IsString(", + "@IsOptional(" + ], + "must_not_contain": [ + "id?:", + "id!:" + ] + }, + "update_dto": { + "path": "server/src/modules/change-equipment-status/dto/update-change-equipment-status.dto.ts", + "must_contain": [ + "from 'class-validator'", + "@IsOptional(", + "equipmentId?:", + "newStatus?:", + "date?:", + "number?:", + "responsible?:" + ] + } + } +} diff --git a/tools/eval/fixtures/change-equipment-status/frontend.assertions.json b/tools/eval/fixtures/change-equipment-status/frontend.assertions.json new file mode 100644 index 0000000..4e2c2b5 --- /dev/null +++ b/tools/eval/fixtures/change-equipment-status/frontend.assertions.json @@ -0,0 +1,68 @@ +{ + "entity": "ChangeEquipmentStatus", + "resource": "change-equipment-statuses", + "files": { + "list": { + "path": "client/src/resources/change-equipment-status/ChangeEquipmentStatusList.tsx", + "must_contain": [ + "List", + "FilterButton", + "ReferenceField", + "SelectField", + "TextField" + ], + "must_match_regex": [ + "ReferenceField[\\s\\S]{0,200}reference=\"equipment\"|reference=\"equipment\"[\\s\\S]{0,200}ReferenceField", + "source=\"newStatus\"" + ] + }, + "create": { + "path": "client/src/resources/change-equipment-status/ChangeEquipmentStatusCreate.tsx", + "must_contain": [ + "Create", + "SimpleForm", + "ReferenceInput", + "AutocompleteInput", + "SelectInput", + "DateInput" + ], + "must_match_regex": [ + "ReferenceInput[\\s\\S]{0,200}reference=\"equipment\"|reference=\"equipment\"[\\s\\S]{0,200}ReferenceInput", + "AutocompleteInput[\\s\\S]{0,200}filterToQuery|filterToQuery[\\s\\S]{0,200}AutocompleteInput", + "SelectInput[\\s\\S]{0,200}source=\"newStatus\"|source=\"newStatus\"[\\s\\S]{0,200}SelectInput", + "DateInput[\\s\\S]{0,200}source=\"date\"|source=\"date\"[\\s\\S]{0,200}DateInput" + ] + }, + "edit": { + "path": "client/src/resources/change-equipment-status/ChangeEquipmentStatusEdit.tsx", + "must_contain": [ + "Edit", + "SimpleForm", + "ReferenceInput", + "AutocompleteInput", + "SelectInput", + "DateInput" + ], + "must_match_regex": [ + "ReferenceInput[\\s\\S]{0,200}reference=\"equipment\"|reference=\"equipment\"[\\s\\S]{0,200}ReferenceInput", + "AutocompleteInput[\\s\\S]{0,200}filterToQuery|filterToQuery[\\s\\S]{0,200}AutocompleteInput", + "SelectInput[\\s\\S]{0,200}source=\"newStatus\"|source=\"newStatus\"[\\s\\S]{0,200}SelectInput", + "DateInput[\\s\\S]{0,200}source=\"date\"|source=\"date\"[\\s\\S]{0,200}DateInput" + ] + }, + "show": { + "path": "client/src/resources/change-equipment-status/ChangeEquipmentStatusShow.tsx", + "must_contain": [ + "Show", + "SimpleShowLayout", + "ReferenceField", + "SelectField", + "DateField" + ], + "must_match_regex": [ + "ReferenceField[\\s\\S]{0,200}reference=\"equipment\"|reference=\"equipment\"[\\s\\S]{0,200}ReferenceField", + "source=\"newStatus\"" + ] + } + } +} diff --git a/tools/eval/fixtures/change-equipment-status/meta.json b/tools/eval/fixtures/change-equipment-status/meta.json new file mode 100644 index 0000000..a2128f0 --- /dev/null +++ b/tools/eval/fixtures/change-equipment-status/meta.json @@ -0,0 +1,15 @@ +{ + "entity": "ChangeEquipmentStatus", + "kebab": "change-equipment-status", + "resource": "change-equipment-statuses", + "description": "Current composite-key status history entity: equipment reference, enum status, date, and optional text metadata", + "tests": [ + "dto-decorator-coverage", + "auth-guards-per-http-method", + "composite-key-route", + "fk-reference-input", + "fk-reference-field", + "content-range-header-pattern", + "enum-filter-in-operator" + ] +} diff --git a/tools/eval/fixtures/equipment/backend.assertions.json b/tools/eval/fixtures/equipment/backend.assertions.json new file mode 100644 index 0000000..99fc2ab --- /dev/null +++ b/tools/eval/fixtures/equipment/backend.assertions.json @@ -0,0 +1,82 @@ +{ + "entity": "Equipment", + "files": { + "controller": { + "path": "server/src/modules/equipment/equipment.controller.ts", + "must_contain": [ + "@Controller('equipment')", + "@UseGuards(", + "JwtAuthGuard", + "RolesGuard", + "@Get()", + "@Post()", + "@Get(':id')", + "@Patch(':id')", + "@Delete(':id')" + ], + "must_not_contain": [ + "@Put(':id')", + "@Post(':id')" + ], + "must_match_regex": [ + "@Delete\\(':id'\\)[\\s\\S]{0,80}@Roles\\('admin'\\)|@Roles\\('admin'\\)[\\s\\S]{0,80}@Delete\\(':id'\\)" + ], + "comment": "Equipment controller must expose the CRUD verbs expected by the DSL-compatible React Admin contract." + }, + "service": { + "path": "server/src/modules/equipment/equipment.service.ts", + "must_contain": [ + "setListHeaders(response", + "_start", + "_end", + "_sort", + "_order", + "q" + ], + "must_match_regex": [ + "contains\\(|mode.*insensitive|insensitive.*mode", + "status.*in\\b|\\bin\\b.*status" + ], + "comment": "Service must translate React Admin list params into Prisma filters and delegate header wiring through the shared helper." + }, + "create_dto": { + "path": "server/src/modules/equipment/dto/create-equipment.dto.ts", + "must_contain": [ + "from 'class-validator'", + "name!:", + "serialNumber!:", + "dateOfInspection?:", + "commissionedAt?:", + "status?:", + "@IsString(", + "@IsOptional(", + "@IsEnum(" + ], + "must_not_contain": [ + "id?:", + "id!:" + ], + "comment": "Required fields use '!' suffix; optional fields use '?' with @IsOptional(); enum fields use @IsEnum(); class-validator must be imported." + }, + "update_dto": { + "path": "server/src/modules/equipment/dto/update-equipment.dto.ts", + "must_contain": [ + "from 'class-validator'", + "name?:", + "serialNumber?:", + "dateOfInspection?:", + "commissionedAt?:", + "status?:", + "@IsOptional(", + "@IsString(", + "@IsEnum(" + ], + "must_not_contain": [ + "name!:", + "serialNumber!:", + "status!:" + ], + "comment": "Update DTO: all fields are optional ('?' suffix with @IsOptional())." + } + } +} diff --git a/tools/eval/fixtures/equipment/frontend.assertions.json b/tools/eval/fixtures/equipment/frontend.assertions.json new file mode 100644 index 0000000..2ecec4c --- /dev/null +++ b/tools/eval/fixtures/equipment/frontend.assertions.json @@ -0,0 +1,67 @@ +{ + "entity": "Equipment", + "resource": "equipment", + "files": { + "list": { + "path": "client/src/resources/equipment/EquipmentList.tsx", + "must_contain": [ + "List", + "FilterButton", + "TextField", + "SelectField", + "name", + "serialNumber" + ], + "must_match_regex": [ + "SelectArrayInput", + "source=\"status\"" + ], + "comment": "Equipment list must expose the current fields, filter UI, and enum filters." + }, + "create": { + "path": "client/src/resources/equipment/EquipmentCreate.tsx", + "must_contain": [ + "Create", + "SimpleForm", + "SelectInput", + "TextInput" + ], + "must_match_regex": [ + "TextInput[\\s\\S]{0,300}source=\"name\"|source=\"name\"[\\s\\S]{0,300}TextInput", + "TextInput[\\s\\S]{0,300}source=\"serialNumber\"|source=\"serialNumber\"[\\s\\S]{0,300}TextInput", + "DateInput[\\s\\S]{0,300}source=\"dateOfInspection\"|source=\"dateOfInspection\"[\\s\\S]{0,300}DateInput", + "DateInput[\\s\\S]{0,300}source=\"commissionedAt\"|source=\"commissionedAt\"[\\s\\S]{0,300}DateInput", + "SelectInput[\\s\\S]{0,300}source=\"status\"|source=\"status\"[\\s\\S]{0,300}SelectInput" + ], + "comment": "Equipment create form must keep type-correct inputs for text, enum, and date fields." + }, + "edit": { + "path": "client/src/resources/equipment/EquipmentEdit.tsx", + "must_contain": [ + "Edit", + "SimpleForm", + "SelectInput", + "TextInput" + ], + "must_match_regex": [ + "TextInput[\\s\\S]{0,300}source=\"name\"|source=\"name\"[\\s\\S]{0,300}TextInput", + "TextInput[\\s\\S]{0,300}source=\"serialNumber\"|source=\"serialNumber\"[\\s\\S]{0,300}TextInput", + "DateInput[\\s\\S]{0,300}source=\"dateOfInspection\"|source=\"dateOfInspection\"[\\s\\S]{0,300}DateInput", + "DateInput[\\s\\S]{0,300}source=\"commissionedAt\"|source=\"commissionedAt\"[\\s\\S]{0,300}DateInput" + ], + "comment": "Equipment edit form must keep the same type-correctness guarantees as create." + }, + "show": { + "path": "client/src/resources/equipment/EquipmentShow.tsx", + "must_contain": [ + "Show", + "SimpleShowLayout", + "TextField", + "SelectField", + "name", + "serialNumber" + ], + "comment": "Show must display the current equipment identity and status fields." + } + } +} diff --git a/tools/eval/fixtures/equipment/meta.json b/tools/eval/fixtures/equipment/meta.json new file mode 100644 index 0000000..11659ec --- /dev/null +++ b/tools/eval/fixtures/equipment/meta.json @@ -0,0 +1,15 @@ +{ + "entity": "Equipment", + "kebab": "equipment", + "resource": "equipment", + "description": "Current equipment entity: UUID primary key, text fields, nullable date fields, and an enum status field", + "tests": [ + "dto-decorator-coverage", + "auth-guards-per-http-method", + "content-range-header-pattern", + "enum-filter-in-operator", + "q-filter-contains-pattern", + "react-admin-component-types", + "class-validator-import" + ] +} diff --git a/tools/eval/run-evals.mjs b/tools/eval/run-evals.mjs new file mode 100644 index 0000000..6aadd7c --- /dev/null +++ b/tools/eval/run-evals.mjs @@ -0,0 +1,184 @@ +#!/usr/bin/env node +/** + * tools/eval/run-evals.mjs + * + * Rule 6 — Eval harness: fixture-based regression tests for generated artifacts. + * + * Philosophy: + * - Evals are the test suite for prompts. Never ship a prompt change without + * running evals first. + * - Use deterministic pattern/regex checks ("reference-free" grading) rather + * than golden snapshot comparison. Patterns are maintainable; snapshots are + * brittle. + * - Eval-driven development: write a failing eval FIRST, then update the prompt + * or re-generate to make it pass. + * + * Usage: + * node tools/eval/run-evals.mjs # run all fixtures + * node tools/eval/run-evals.mjs --entity equipment + * node tools/eval/run-evals.mjs --verbose + */ + +import { existsSync, readFileSync, readdirSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const rootDir = path.resolve(__dirname, '../..'); +const fixturesDir = path.join(__dirname, 'fixtures'); + +const args = new Set(process.argv.slice(2)); +const verbose = args.has('--verbose') || args.has('-v'); +const entityFilter = (() => { + const idx = process.argv.indexOf('--entity'); + return idx !== -1 ? process.argv[idx + 1] : null; +})(); + +// --------------------------------------------------------------------------- +// Assertion engine +// --------------------------------------------------------------------------- + +let totalChecks = 0; +let totalFailures = 0; +const failures = []; + +function readArtifact(relativePath) { + const filePath = path.join(rootDir, relativePath); + if (!existsSync(filePath)) return null; + return readFileSync(filePath, 'utf8'); +} + +function runFileAssertions(filePath, fileSpec, entityLabel) { + const content = readArtifact(filePath); + + if (content === null) { + totalChecks++; + totalFailures++; + failures.push({ entity: entityLabel, file: filePath, check: 'file-exists', result: 'FAIL', detail: `File not found: ${filePath}` }); + return; + } + + if (verbose) { + console.log(` [${entityLabel}] Checking ${filePath}`); + } + + for (const expected of fileSpec.must_contain ?? []) { + totalChecks++; + if (!content.includes(expected)) { + totalFailures++; + failures.push({ entity: entityLabel, file: filePath, check: 'must_contain', result: 'FAIL', detail: `Missing: ${expected}` }); + } + } + + for (const forbidden of fileSpec.must_not_contain ?? []) { + totalChecks++; + if (content.includes(forbidden)) { + totalFailures++; + failures.push({ entity: entityLabel, file: filePath, check: 'must_not_contain', result: 'FAIL', detail: `Forbidden pattern found: ${forbidden}` }); + } + } + + for (const patternStr of fileSpec.must_match_regex ?? []) { + totalChecks++; + try { + const re = new RegExp(patternStr); + if (!re.test(content)) { + totalFailures++; + failures.push({ entity: entityLabel, file: filePath, check: 'must_match_regex', result: 'FAIL', detail: `Regex not matched: ${patternStr}` }); + } + } catch (e) { + totalFailures++; + failures.push({ entity: entityLabel, file: filePath, check: 'must_match_regex', result: 'ERROR', detail: `Bad regex: ${patternStr} — ${e.message}` }); + } + } + + for (const patternStr of fileSpec.must_not_match_regex ?? []) { + totalChecks++; + try { + const re = new RegExp(patternStr); + if (re.test(content)) { + totalFailures++; + failures.push({ entity: entityLabel, file: filePath, check: 'must_not_match_regex', result: 'FAIL', detail: `Forbidden regex matched: ${patternStr}` }); + } + } catch (e) { + totalFailures++; + failures.push({ entity: entityLabel, file: filePath, check: 'must_not_match_regex', result: 'ERROR', detail: `Bad regex: ${patternStr} — ${e.message}` }); + } + } +} + +function runFixture(fixtureDir) { + const metaPath = path.join(fixtureDir, 'meta.json'); + if (!existsSync(metaPath)) return; + + const meta = JSON.parse(readFileSync(metaPath, 'utf8')); + const { entity, kebab } = meta; + + if (entityFilter && kebab !== entityFilter && entity.toLowerCase() !== entityFilter.toLowerCase()) { + return; + } + + if (verbose) { + console.log(`\n[EVAL] ${entity} — ${meta.description ?? ''}`); + } + + const backendPath = path.join(fixtureDir, 'backend.assertions.json'); + if (existsSync(backendPath)) { + const spec = JSON.parse(readFileSync(backendPath, 'utf8')); + for (const [key, fileSpec] of Object.entries(spec.files ?? {})) { + runFileAssertions(fileSpec.path, fileSpec, `${entity}/${key}`); + } + } + + const frontendPath = path.join(fixtureDir, 'frontend.assertions.json'); + if (existsSync(frontendPath)) { + const spec = JSON.parse(readFileSync(frontendPath, 'utf8')); + for (const [key, fileSpec] of Object.entries(spec.files ?? {})) { + runFileAssertions(fileSpec.path, fileSpec, `${entity}/${key}`); + } + } +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + +const fixtureDirs = readdirSync(fixturesDir, { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => path.join(fixturesDir, d.name)); + +for (const dir of fixtureDirs) { + runFixture(dir); +} + +// --------------------------------------------------------------------------- +// Report +// --------------------------------------------------------------------------- + +console.log(''); +console.log('══════════════════════════════════════════════'); +console.log(' KIS-TOiR Eval Report'); +console.log('══════════════════════════════════════════════'); +console.log(` Fixtures: ${fixtureDirs.length}`); +console.log(` Checks: ${totalChecks}`); +console.log(` Passed: ${totalChecks - totalFailures}`); +console.log(` Failed: ${totalFailures}`); +console.log('══════════════════════════════════════════════'); + +if (failures.length > 0) { + console.log(''); + console.log('Failures:'); + for (const f of failures) { + console.log(` [${f.result}] ${f.entity} — ${f.file}`); + console.log(` ${f.check}: ${f.detail}`); + } + console.log(''); + console.log('To fix: update the prompt or re-generate the failing entity, then re-run evals.'); + console.log('To update a fixture (intentional change): edit tools/eval/fixtures//*.assertions.json'); + console.log(''); + process.exit(1); +} + +console.log(''); +console.log('All evals passed.'); +console.log(''); diff --git a/tools/generate-api-summary.mjs b/tools/generate-api-summary.mjs new file mode 100644 index 0000000..5cbfc7f --- /dev/null +++ b/tools/generate-api-summary.mjs @@ -0,0 +1,13 @@ +import { mkdirSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { buildApiSummary } from './api-summary.mjs'; + +const rootDir = process.cwd(); +const outputPath = path.join(rootDir, 'api-summary.json'); + +mkdirSync(path.dirname(outputPath), { recursive: true }); + +const summary = buildApiSummary(rootDir); +writeFileSync(outputPath, `${JSON.stringify(summary, null, 2)}\n`, 'utf8'); + +console.log(`Generated ${path.relative(rootDir, outputPath)}`); diff --git a/tools/hooks/pre-commit b/tools/hooks/pre-commit new file mode 100644 index 0000000..a0a36a6 --- /dev/null +++ b/tools/hooks/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +# Pre-commit hook: runs the generation validation gate and eval harness. +# Install with: npm run install-hooks + +node tools/validate-generation.mjs --artifacts-only && node tools/eval/run-evals.mjs diff --git a/tools/install-hooks.mjs b/tools/install-hooks.mjs new file mode 100644 index 0000000..227319d --- /dev/null +++ b/tools/install-hooks.mjs @@ -0,0 +1,19 @@ +import { copyFileSync, chmodSync, mkdirSync, existsSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, '..'); +const hooksDir = path.join(root, '.git', 'hooks'); +const src = path.join(root, 'tools', 'hooks', 'pre-commit'); +const dest = path.join(hooksDir, 'pre-commit'); + +if (!existsSync(path.join(root, '.git'))) { + console.error('Not a git repository. Run from the repo root.'); + process.exit(1); +} + +mkdirSync(hooksDir, { recursive: true }); +copyFileSync(src, dest); +try { chmodSync(dest, 0o755); } catch { /* Windows */ } +console.log('Installed pre-commit hook → .git/hooks/pre-commit'); diff --git a/tools/validate-generation.mjs b/tools/validate-generation.mjs new file mode 100644 index 0000000..5072ba3 --- /dev/null +++ b/tools/validate-generation.mjs @@ -0,0 +1,502 @@ +import { existsSync, readFileSync, readdirSync } from 'node:fs'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { getApiDslFiles, buildApiSummary } from './api-summary.mjs'; + +const rootDir = process.cwd(); +const args = new Set(process.argv.slice(2)); +const artifactsOnly = args.has('--artifacts-only'); +const runRuntime = args.has('--run-runtime'); + +const failures = []; +const warnings = []; + +function assertCondition(condition, message) { + if (!condition) { + failures.push(message); + } +} + +function warn(message) { + warnings.push(message); +} + +function readIfExists(relativePath) { + const filePath = path.join(rootDir, relativePath); + if (!existsSync(filePath)) { + return null; + } + + return readFileSync(filePath, 'utf8'); +} + +function requireFile(relativePath) { + assertCondition(existsSync(path.join(rootDir, relativePath)), `Missing file: ${relativePath}`); +} + +function requireFiles(relativePaths) { + relativePaths.forEach(requireFile); +} + +function requireContent(relativePath, pattern, message) { + const contents = readIfExists(relativePath); + assertCondition(Boolean(contents), `Missing file: ${relativePath}`); + if (!contents) { + return; + } + + assertCondition(pattern.test(contents), `${message} (${relativePath})`); +} + +function parseJson(relativePath) { + const raw = readIfExists(relativePath); + if (!raw) { + failures.push(`Missing file: ${relativePath}`); + return null; + } + + try { + return JSON.parse(raw); + } catch (error) { + failures.push(`Invalid JSON in ${relativePath}: ${error.message}`); + return null; + } +} + +function getRealmArtifactPath() { + const rootFiles = readdirSync(rootDir, { withFileTypes: true }) + .filter((entry) => entry.isFile()) + .map((entry) => entry.name); + + const realmArtifacts = rootFiles.filter((entry) => /-realm\.json$/i.test(entry)); + assertCondition(realmArtifacts.length === 1, 'Expected exactly one root-level *-realm.json artifact'); + + return realmArtifacts[0] ?? null; +} + +function getWorkspaceInfo() { + return { + server: { + dir: path.join(rootDir, 'server'), + packagePath: 'server/package.json', + scaffoldFiles: [ + 'server/package.json', + 'server/tsconfig.json', + 'server/tsconfig.build.json', + 'server/nest-cli.json', + 'server/src/main.ts', + 'server/src/app.module.ts', + ], + }, + client: { + dir: path.join(rootDir, 'client'), + packagePath: 'client/package.json', + scaffoldFiles: [ + 'client/package.json', + 'client/index.html', + 'client/tsconfig.json', + 'client/tsconfig.node.json', + 'client/vite.config.ts', + 'client/src/main.tsx', + 'client/src/vite-env.d.ts', + ], + }, + }; +} + +function validateBuildChecks() { + requireFiles([ + 'README.md', + 'package.json', + 'domain/dsl-spec.md', + 'api-summary.json', + 'server/prisma/schema.prisma', + 'docker-compose.yml', + 'server/Dockerfile', + 'client/Dockerfile', + 'client/nginx/default.conf', + 'server/.env.example', + 'client/.env.example', + 'prompts/general-prompt.md', + 'prompts/auth-rules.md', + 'prompts/backend-rules.md', + 'prompts/frontend-rules.md', + 'prompts/runtime-rules.md', + 'prompts/validation-rules.md', + ]); + + // rule: AGENTS.md §Tier-1 — api.dsl must exist + const apiDslFiles = getApiDslFiles(rootDir); + assertCondition(apiDslFiles.length > 0, 'Expected at least one domain/*.api.dsl file'); + + // rule: AGENTS.md §Tier-2 — api-summary.json must match parsed api.dsl + const actualApiSummaryRaw = readIfExists('api-summary.json'); + if (actualApiSummaryRaw) { + try { + const expectedApiSummary = JSON.stringify(buildApiSummary(rootDir), null, 2); + assertCondition( + actualApiSummaryRaw.trim() === expectedApiSummary, + 'api-summary.json is out of date. Run `npm run generate:api-summary`.', + ); + } catch (error) { + failures.push(`api-summary.json freshness check failed: ${error.message}`); + } + } + + const { server, client } = getWorkspaceInfo(); + requireFiles(server.scaffoldFiles); + requireFiles(client.scaffoldFiles); + + const serverPackage = parseJson(server.packagePath); + if (serverPackage) { + assertCondition(serverPackage.scripts?.build === 'nest build', 'server/package.json must keep `build = nest build`'); + assertCondition(serverPackage.scripts?.start === 'nest start', 'server/package.json must keep `start = nest start`'); + assertCondition(serverPackage.scripts?.['start:dev'] === 'nest start --watch', 'server/package.json must keep `start:dev = nest start --watch`'); + assertCondition(Boolean(serverPackage.scripts?.['start:prod']), 'server/package.json must define a start:prod script'); + assertCondition(Boolean(serverPackage.dependencies?.['@nestjs/core']), 'server/package.json must keep Nest runtime dependencies'); + } + + const clientPackage = parseJson(client.packagePath); + if (clientPackage) { + assertCondition(clientPackage.scripts?.dev === 'vite', 'client/package.json must keep `dev = vite`'); + assertCondition(clientPackage.scripts?.build === 'vite build', 'client/package.json must keep `build = vite build`'); + assertCondition(clientPackage.scripts?.preview === 'vite preview', 'client/package.json must keep `preview = vite preview`'); + assertCondition(Boolean(clientPackage.devDependencies?.vite), 'client/package.json must keep Vite as a dev dependency'); + assertCondition(Boolean(clientPackage.devDependencies?.['@vitejs/plugin-react']), 'client/package.json must keep @vitejs/plugin-react as a dev dependency'); + } +} + +function validateAuthChecks() { + requireFiles([ + 'client/src/auth/keycloak.ts', + 'client/src/auth/authProvider.ts', + 'client/src/dataProvider.ts', + 'client/src/config/env.ts', + 'client/src/main.tsx', + 'client/src/App.tsx', + 'server/src/auth/auth.module.ts', + 'server/src/auth/auth.service.ts', + 'server/src/auth/guards/jwt-auth.guard.ts', + 'server/src/auth/guards/roles.guard.ts', + 'server/src/auth/decorators/public.decorator.ts', + 'server/src/auth/decorators/roles.decorator.ts', + ]); + + requireContent( + 'client/src/auth/keycloak.ts', + /onLoad:\s*'login-required'/, + 'Frontend auth must initialize Keycloak with login-required', + ); + requireContent( + 'client/src/auth/keycloak.ts', + /pkceMethod:\s*'S256'/, + 'Frontend auth must use PKCE S256', + ); + requireContent( + 'client/src/auth/keycloak.ts', + /updateToken\(/, + 'Frontend auth must refresh access tokens through the Keycloak adapter', + ); + + const keycloakSource = readIfExists('client/src/auth/keycloak.ts') ?? ''; + assertCondition( + !/loadUserProfile\(/.test(keycloakSource), + 'Frontend auth must not call keycloak.loadUserProfile()', + ); + + requireContent( + 'client/src/dataProvider.ts', + /Authorization', `Bearer \$\{token\}`/, + 'dataProvider must attach bearer tokens in the shared request seam', + ); + + const authProvider = readIfExists('client/src/auth/authProvider.ts') ?? ''; + assertCondition( + /status === 401/.test(authProvider) && /status === 403/.test(authProvider), + 'authProvider must distinguish 401 and 403 semantics', + ); + + const authService = readIfExists('server/src/auth/auth.service.ts') ?? ''; + assertCondition( + /jwtVerify/.test(authService) && /KEYCLOAK_ISSUER_URL/.test(authService) && /KEYCLOAK_AUDIENCE/.test(authService), + 'Backend auth must verify JWTs with issuer and audience', + ); + assertCondition(/realm_access/.test(authService), 'Backend auth must extract roles from realm_access.roles'); + assertCondition(/KEYCLOAK_JWKS_URL/.test(authService), 'Backend auth must support explicit KEYCLOAK_JWKS_URL'); + assertCondition(/\.well-known\/openid-configuration/.test(authService), 'Backend auth must try OIDC discovery before fallback certs'); + assertCondition(/protocol\/openid-connect\/certs/.test(authService), 'Backend auth must keep Keycloak certs fallback resolution'); +} + +function validateRealmChecks() { + const realmArtifactName = getRealmArtifactPath(); + if (!realmArtifactName) { + return; + } + + const artifact = parseJson(realmArtifactName); + if (!artifact) { + return; + } + + const realmRoles = artifact.roles?.realm?.map((role) => role.name) ?? []; + const frontendClient = artifact.clients?.find((client) => client.clientId?.endsWith('-frontend')); + const backendClient = artifact.clients?.find((client) => client.clientId?.endsWith('-backend')); + const audienceScope = artifact.clientScopes?.find((scope) => scope.name === 'api-audience'); + + ['admin', 'editor', 'viewer'].forEach((role) => { + assertCondition(realmRoles.includes(role), `Realm artifact must define realm role ${role}`); + }); + + assertCondition(Boolean(frontendClient), 'Realm artifact must define the frontend SPA client'); + assertCondition(Boolean(backendClient), 'Realm artifact must define the backend resource client'); + assertCondition(Boolean(audienceScope), 'Realm artifact must define the api-audience client scope'); + + if (frontendClient) { + assertCondition(frontendClient.publicClient === true, 'Frontend realm client must be public'); + assertCondition( + frontendClient.standardFlowEnabled === true && + frontendClient.implicitFlowEnabled === false && + frontendClient.directAccessGrantsEnabled === false, + 'Frontend realm client must use standard flow only', + ); + assertCondition( + frontendClient.attributes?.['pkce.code.challenge.method'] === 'S256', + 'Frontend realm client must enforce PKCE S256', + ); + + const mapperNames = new Set((frontendClient.protocolMappers ?? []).map((mapper) => mapper.name)); + ['sub', 'preferred_username', 'email', 'name', 'realm roles'].forEach((mapperName) => { + assertCondition(mapperNames.has(mapperName), `Frontend realm client must include protocol mapper ${mapperName}`); + }); + } + + if (backendClient && audienceScope) { + const audienceMapper = (audienceScope.protocolMappers ?? []).find( + (mapper) => mapper.protocolMapper === 'oidc-audience-mapper', + ); + assertCondition(Boolean(audienceMapper), 'api-audience scope must include an audience mapper'); + assertCondition( + audienceMapper?.config?.['included.client.audience'] === backendClient.clientId, + 'api-audience scope must deliver the backend audience/client id', + ); + assertCondition(backendClient.bearerOnly === true, 'Backend realm client must be bearer-only'); + } +} + +function validateRuntimeContractChecks() { + requireFiles([ + 'docker-compose.yml', + 'server/Dockerfile', + 'client/Dockerfile', + 'client/nginx/default.conf', + ]); + const compose = readIfExists('docker-compose.yml') ?? ''; + assertCondition(/image:\s*postgres:16/.test(compose), 'docker-compose must provision postgres:16'); + assertCondition(/^\s{2}server\s*:/m.test(compose), 'docker-compose must define a server service'); + assertCondition(/^\s{2}client\s*:/m.test(compose), 'docker-compose must define a client service'); + assertCondition( + /^\s{4}ports:\n\s{6}-\s*"\$\{CLIENT_PORT:-8080\}:80"/m.test(compose), + 'docker-compose client service must publish the SPA on ${CLIENT_PORT:-8080}:80 by default', + ); + const hasKeycloakService = + /^\s{2}keycloak\s*:/m.test(compose) || /image:\s*.*keycloak/i.test(compose); + assertCondition(!hasKeycloakService, 'docker-compose must remain PostgreSQL-only (no Keycloak container)'); + + const codexConfig = readIfExists('.codex/config.toml') ?? ''; + if (codexConfig) { + const agentConfigPaths = [...codexConfig.matchAll(/config_file\s*=\s*"([^"]+)"/g)].map((match) => match[1]); + for (const configPath of agentConfigPaths) { + assertCondition(!path.isAbsolute(configPath), `.codex/config.toml must not hardcode absolute config_file paths: ${configPath}`); + const relativeToCodex = path.join('.codex', configPath); + assertCondition( + existsSync(path.join(rootDir, relativeToCodex)), + `.codex/config.toml references a missing agent config: ${relativeToCodex}`, + ); + } + } + + const serverDockerfile = readIfExists('server/Dockerfile') ?? ''; + assertCondition(/FROM node:/i.test(serverDockerfile), 'server/Dockerfile must build from a Node base image'); + assertCondition(/npm run build/.test(serverDockerfile), 'server/Dockerfile must build the Nest workspace'); + assertCondition(/docker-entrypoint\.sh/.test(serverDockerfile), 'server/Dockerfile must use the repository entrypoint'); + + const clientDockerfile = readIfExists('client/Dockerfile') ?? ''; + assertCondition(/FROM nginx:/i.test(clientDockerfile), 'client/Dockerfile must use nginx for runtime serving'); + assertCondition(/COPY nginx\/default\.conf/.test(clientDockerfile), 'client/Dockerfile must install the nginx proxy config'); + assertCondition(/COPY --from=build \/app\/dist/.test(clientDockerfile), 'client/Dockerfile must copy the built SPA bundle'); + + const nginxConfig = readIfExists('client/nginx/default.conf') ?? ''; + assertCondition(/location \/api\//.test(nginxConfig), 'client/nginx/default.conf must proxy /api requests'); + assertCondition(/try_files \$uri \$uri\/ \/index\.html;/.test(nginxConfig), 'client/nginx/default.conf must preserve SPA routing'); + assertCondition(/proxy_pass/.test(nginxConfig), 'client/nginx/default.conf must proxy backend traffic'); + + const serverEnvExample = readIfExists('server/.env.example') ?? ''; + assertCondition( + /KEYCLOAK_ISSUER_URL="https:\/\/sso\.greact\.ru\/realms\/toir"/.test(serverEnvExample), + 'server/.env.example must keep the working Keycloak issuer example', + ); + assertCondition( + /KEYCLOAK_AUDIENCE="?toir-backend"?/.test(serverEnvExample), + 'server/.env.example must keep the working backend audience example', + ); + assertCondition( + /CORS_ALLOWED_ORIGINS="http:\/\/localhost:5173,https:\/\/toir-frontend\.greact\.ru"/.test(serverEnvExample), + 'server/.env.example must keep the working CORS example with the production frontend domain', + ); + assertCondition( + !/KEYCLOAK_ISSUER_URL=http:\/\/localhost:8080\/realms\/toir/.test(serverEnvExample), + 'server/.env.example must not regress to localhost Keycloak as the baseline issuer example', + ); + + const clientEnvExample = readIfExists('client/.env.example') ?? ''; + assertCondition( + /VITE_KEYCLOAK_URL=https:\/\/sso\.greact\.ru/.test(clientEnvExample), + 'client/.env.example must keep the working domain-based Keycloak URL example', + ); + assertCondition( + /VITE_KEYCLOAK_REALM=toir/.test(clientEnvExample) && /VITE_KEYCLOAK_CLIENT_ID=toir-frontend/.test(clientEnvExample), + 'client/.env.example must keep the working realm and frontend client examples', + ); + assertCondition( + !/VITE_KEYCLOAK_URL=http:\/\/localhost:8080/.test(clientEnvExample), + 'client/.env.example must not regress to localhost Keycloak as the baseline example', + ); + + const healthController = readIfExists('server/src/health/health.controller.ts') ?? ''; + assertCondition(Boolean(healthController), 'Missing file: server/src/health/health.controller.ts'); + if (healthController) { + assertCondition( + /@Public\(\)/.test(healthController) && /@Controller\('health'\)/.test(healthController), + '/health must stay public', + ); + } +} + +function runCommand(command, commandArgs, workdir, failureLabel) { + const runtimeEnv = { ...process.env }; + const envExamplePath = path.join(workdir, '.env.example'); + if (existsSync(envExamplePath)) { + const envExample = readFileSync(envExamplePath, 'utf8'); + for (const line of envExample.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) { + continue; + } + + const separator = trimmed.indexOf('='); + if (separator <= 0) { + continue; + } + + const key = trimmed.slice(0, separator).trim(); + const value = trimmed.slice(separator + 1).trim().replace(/^"|"$/g, ''); + if (!(key in runtimeEnv)) { + runtimeEnv[key] = value; + } + } + } + + const commandLine = [command, ...commandArgs].join(' '); + const result = spawnSync(commandLine, { + cwd: workdir, + encoding: 'utf8', + stdio: 'pipe', + shell: true, + env: runtimeEnv, + }); + + if (result.error) { + failures.push(`${failureLabel}: ${commandLine}\n${result.error.message}`); + return false; + } + + if (result.status !== 0) { + const stderr = result.stderr?.trim(); + const stdout = result.stdout?.trim(); + failures.push( + `${failureLabel}: ${commandLine}${stderr ? `\n${stderr}` : stdout ? `\n${stdout}` : ''}`, + ); + return false; + } + + return true; +} + +function maybeValidateWorkspaceBuild(relativeDir) { + const workspaceDir = path.join(rootDir, relativeDir); + if (!existsSync(path.join(workspaceDir, 'package.json'))) { + failures.push(`Missing file: ${relativeDir}/package.json`); + return; + } + + if (!existsSync(path.join(workspaceDir, 'node_modules'))) { + warn(`Skipped build verification for ${relativeDir}: install dependencies in ${relativeDir}/ to validate workspace buildability.`); + return; + } + + runCommand('npm', ['run', 'build'], workspaceDir, `Build verification failed in ${relativeDir}`); +} + +function validateBuildExecutionChecks() { + maybeValidateWorkspaceBuild('server'); + maybeValidateWorkspaceBuild('client'); +} + +function validateRuntimeExecutionChecks() { + const serverDir = path.join(rootDir, 'server'); + if (!existsSync(path.join(serverDir, 'node_modules'))) { + failures.push( + 'Runtime validation requires installed backend dependencies. Run `npm install` in server/ before `npm run validate:generation:runtime`.', + ); + return; + } + + runCommand('npx', ['prisma', 'generate'], serverDir, 'Prisma generate failed'); + const migrationsDir = path.join(serverDir, 'prisma', 'migrations'); + const hasMigrations = + existsSync(migrationsDir) && + readdirSync(migrationsDir, { withFileTypes: true }).some((entry) => entry.isDirectory()); + + if (hasMigrations) { + runCommand('npx', ['prisma', 'migrate', 'deploy'], serverDir, 'Prisma migrate deploy failed'); + } else { + warn('No committed Prisma migrations found; runtime validation is using `prisma db push` as a temporary bootstrap fallback.'); + runCommand('npx', ['prisma', 'db', 'push'], serverDir, 'Prisma db push failed'); + } + runCommand('npx', ['prisma', 'db', 'seed'], serverDir, 'Prisma seed failed'); +} + +// --------------------------------------------------------------------------- +// Structural validator scope only: +// keep stable scaffold/runtime/auth/realm/build invariants here. +// Entity-level DSL fidelity and CRUD/UI semantics are owned by `npm run eval:generation`. +// --------------------------------------------------------------------------- + +validateBuildChecks(); +validateAuthChecks(); +validateRealmChecks(); +validateRuntimeContractChecks(); + +if (!artifactsOnly) { + validateBuildExecutionChecks(); +} + +if (!artifactsOnly && runRuntime) { + validateRuntimeExecutionChecks(); +} else if (!artifactsOnly) { + warn('Runtime command execution skipped. Use --run-runtime after installing dependencies and starting the local database.'); +} + +for (const warning of warnings) { + console.warn(`WARN: ${warning}`); +} + +if (failures.length > 0) { + console.error('Generation validation failed:'); + for (const failure of failures) { + console.error(`- ${failure}`); + } + process.exit(1); +} + +console.log('Generation validation passed.');