From 8ca418d653b9428e1f6c84ecbee3e72097afa69c Mon Sep 17 00:00:00 2001 From: Brian Li Date: Tue, 22 Sep 2026 22:48:23 -0700 Subject: [PATCH 01/29] feat: wire MongoDB and prepare developer setup --- .github/workflows/ci.yml | 4 +-- AGENTS.md | 13 ++++++++++ README.md | 29 ++++++++++++++------- docs/agents/domain.md | 12 +++++++++ docs/agents/issue-tracker.md | 26 +++++++++++++++++++ docs/agents/triage-labels.md | 13 ++++++++++ src/app/api/example/route.ts | 2 ++ src/database/db.ts | 49 ++++++++++++++++++++++++++++-------- 8 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 AGENTS.md create mode 100644 docs/agents/domain.md create mode 100644 docs/agents/issue-tracker.md create mode 100644 docs/agents/triage-labels.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c932ac1..a973270 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,9 +3,9 @@ name: ci # Controls when the workflow will run on: push: - branches: ["main", "develop"] + branches: ["develop"] pull_request: - branches: ["main", "develop"] + branches: ["develop"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..33131d8 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,13 @@ +## Agent skills + +### Issue tracker + +Issues and specs are tracked in GitHub Issues for `BrianLiDesign/coffee-shop`. See `docs/agents/issue-tracker.md`. + +### Triage labels + +Use the default canonical labels: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, and `wontfix`. See `docs/agents/triage-labels.md`. + +### Domain docs + +This is a single-context repository using a root `CONTEXT.md` and `docs/adr/`. See `docs/agents/domain.md`. diff --git a/README.md b/README.md index 130bf99..6512f66 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# name of non-profit +# Coffee Shop -{One sentence description of the project} +A coffee shop web application that helps customers explore drinks and food, customize orders, and choose convenient pickup or delivery options. ## Table of Contents - [Overview](#overview) - [Purpose](#purpose) + - [Planned Features](#planned-features) - [Team](#team) - [Getting Started And Contributing](#getting-started-and-contributing) @@ -13,17 +14,27 @@ ### Purpose -{Short paragraph description of the non-profit mission and the purpose of this project. Who will this project help, why is it necessary. Impact of the project.} +This project gives coffee shop customers a clear way to learn about the shop, explore its menu, and find convenient ordering options. It will present drink ingredients and prices, support drink customization, highlight seasonal food and drink offerings, tell the story of the shop, and show available discounts. + +### Planned Features + +- Browse drinks with ingredients and prices +- Customize drinks before ordering +- Browse food and seasonal food and drink offerings +- Read the coffee shop story +- Choose pickup or delivery through Grubhub or DoorDash +- See available discounts ### Team -The {non-profit name} team consists of {#} Cal Poly students. Over the course of about 9 months, we worked as a team to deploy this web application. The team members are listed below: +The Coffee Shop team consists of six students. Team responsibilities are listed below: -- [First Last](https://www.linkedin.com/) - Project Manager -- [First Last](https://www.linkedin.com/) - Designer -- [First Last](https://www.linkedin.com/) - Tech Lead -- [First Last](https://www.linkedin.com/) - Tech Lead -- [First Last](https://www.linkedin.com/) - Software Developer +- Brian - Project tech lead for the overall project +- Jean - Backend; strengths in Java, Python, and TypeScript; interested in team experience and backend work +- Naomi - Database; also interested in technical team projects, web development, and database work +- Vedika - Frontend; strengths in Java and Python; interested in GitHub collaboration and design +- Aditi - Frontend; strengths in HTML, CSS, Java, and Python +- Sofie - Backend; strengths in HTML, CSS, and Python; interested in collaboration and backend work ## Getting Started And Contributing diff --git a/docs/agents/domain.md b/docs/agents/domain.md new file mode 100644 index 0000000..4ce3107 --- /dev/null +++ b/docs/agents/domain.md @@ -0,0 +1,12 @@ +# Domain Docs + +This is a single-context repository. Engineering skills should consult the following files when they exist: + +- `CONTEXT.md` at the repository root. +- Relevant decisions in `docs/adr/`. + +If these files do not exist, proceed without flagging their absence. The domain-modeling flow creates them lazily when terms or decisions need to be recorded. + +Use the glossary vocabulary from `CONTEXT.md` when naming domain concepts in issues, proposals, refactors, and tests. If a needed concept is not defined, note the gap rather than silently introducing competing terminology. + +If an implementation or proposal contradicts an existing ADR, surface the conflict explicitly rather than silently overriding it. diff --git a/docs/agents/issue-tracker.md b/docs/agents/issue-tracker.md new file mode 100644 index 0000000..8db2269 --- /dev/null +++ b/docs/agents/issue-tracker.md @@ -0,0 +1,26 @@ +# Issue tracker: GitHub + +Issues and specs for this repo live as GitHub issues in `BrianLiDesign/coffee-shop`. Use the `gh` CLI for issue operations. + +## Conventions + +- Create an issue with `gh issue create --title "..." --body "..."`. +- Read an issue with `gh issue view --comments`. +- List issues with `gh issue list --state open` and add label or JSON filters as needed. +- Comment on an issue with `gh issue comment --body "..."`. +- Apply or remove labels with `gh issue edit --add-label "..."` or `--remove-label "..."`. +- Close an issue with `gh issue close --comment "..."`. + +Infer the repository from the GitHub remote; `gh` does this automatically from the repository checkout. + +## Pull requests as a triage surface + +**PRs as a request surface: no.** External pull requests are not included in the triage queue by default. + +## When a skill says "publish to the issue tracker" + +Create a GitHub issue. + +## When a skill says "fetch the relevant ticket" + +Run `gh issue view --comments`. diff --git a/docs/agents/triage-labels.md b/docs/agents/triage-labels.md new file mode 100644 index 0000000..05c6992 --- /dev/null +++ b/docs/agents/triage-labels.md @@ -0,0 +1,13 @@ +# Triage Labels + +The engineering skills use five canonical triage roles. These are the label strings used in this repository's GitHub issue tracker. + +| Label in mattpocock/skills | Label in this repository | Meaning | +| -------------------------- | ------------------------ | -------------------------------------------- | +| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue | +| `needs-info` | `needs-info` | Waiting on the reporter for more information | +| `ready-for-agent` | `ready-for-agent` | Fully specified and ready for an AFK agent | +| `ready-for-human` | `ready-for-human` | Requires human implementation | +| `wontfix` | `wontfix` | Will not be actioned | + +When a skill mentions a canonical role, use the corresponding label shown above. diff --git a/src/app/api/example/route.ts b/src/app/api/example/route.ts index 52fd379..1eed28a 100644 --- a/src/app/api/example/route.ts +++ b/src/app/api/example/route.ts @@ -1,6 +1,8 @@ import connectDB from "@/database/db"; import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + /** * Example GET API route * @returns {message: string} diff --git a/src/database/db.ts b/src/database/db.ts index c54cfb5..4b523e1 100644 --- a/src/database/db.ts +++ b/src/database/db.ts @@ -1,20 +1,47 @@ import mongoose from "mongoose"; -const url: string = process.env.MONGO_URI as string; -let connection: typeof mongoose; +interface MongooseCache { + conn: typeof mongoose | null; + promise: Promise | null; +} + +declare global { + var mongooseCache: MongooseCache | undefined; +} + +const cached: MongooseCache = globalThis.mongooseCache ?? { + conn: null, + promise: null, +}; + +globalThis.mongooseCache = cached; /** - * Makes a connection to a MongoDB database. If a connection already exists, does nothing - * Call this function before all api routes - * @returns {Promise} + * Makes a connection to MongoDB and reuses it across API requests and hot reloads. */ -const connectDB = async () => { - if (!connection) { - // uncomment this line once you have the MONGO_URI set up - // connection = await mongoose.connect(url); - connection = "remove me" as any; // remove me - return connection; +const connectDB = async (): Promise => { + const url = process.env.MONGO_URI; + + if (!url) { + throw new Error("MONGO_URI is not defined"); + } + + if (cached.conn) { + return cached.conn; + } + + if (!cached.promise) { + cached.promise = mongoose.connect(url).then(() => mongoose); } + + try { + cached.conn = await cached.promise; + } catch (error) { + cached.promise = null; + throw error; + } + + return cached.conn; }; export default connectDB; From 5d2190eb54ff8adef890b8c5829aabff9c8660aa Mon Sep 17 00:00:00 2001 From: Brian Li Date: Tue, 22 Sep 2026 22:48:38 -0700 Subject: [PATCH 02/29] ci: validate main and develop branches --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a973270..c932ac1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,9 +3,9 @@ name: ci # Controls when the workflow will run on: push: - branches: ["develop"] + branches: ["main", "develop"] pull_request: - branches: ["develop"] + branches: ["main", "develop"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: From a4e18c95855d1e532b80638d452e0fe73a9c4e11 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Tue, 22 Sep 2026 22:49:04 -0700 Subject: [PATCH 03/29] style: normalize CI workflow formatting --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c932ac1..0afb38c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,6 @@ jobs: # - Settings -> Secrets and variables -> New repository secret MONGO_URI: ${{ secrets.MONGO_URI }} # Add additional environment variables here - + - name: Run tests run: npm test From 86265a452aeb3eec0f36cc8cebfd2544fce9d984 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Wed, 23 Sep 2026 18:10:01 -0700 Subject: [PATCH 04/29] docs: remove obsolete triage label guidance --- AGENTS.md | 4 ---- docs/agents/triage-labels.md | 13 ------------- 2 files changed, 17 deletions(-) delete mode 100644 docs/agents/triage-labels.md diff --git a/AGENTS.md b/AGENTS.md index 33131d8..4b28c30 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,10 +4,6 @@ Issues and specs are tracked in GitHub Issues for `BrianLiDesign/coffee-shop`. See `docs/agents/issue-tracker.md`. -### Triage labels - -Use the default canonical labels: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, and `wontfix`. See `docs/agents/triage-labels.md`. - ### Domain docs This is a single-context repository using a root `CONTEXT.md` and `docs/adr/`. See `docs/agents/domain.md`. diff --git a/docs/agents/triage-labels.md b/docs/agents/triage-labels.md deleted file mode 100644 index 05c6992..0000000 --- a/docs/agents/triage-labels.md +++ /dev/null @@ -1,13 +0,0 @@ -# Triage Labels - -The engineering skills use five canonical triage roles. These are the label strings used in this repository's GitHub issue tracker. - -| Label in mattpocock/skills | Label in this repository | Meaning | -| -------------------------- | ------------------------ | -------------------------------------------- | -| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue | -| `needs-info` | `needs-info` | Waiting on the reporter for more information | -| `ready-for-agent` | `ready-for-agent` | Fully specified and ready for an AFK agent | -| `ready-for-human` | `ready-for-human` | Requires human implementation | -| `wontfix` | `wontfix` | Will not be actioned | - -When a skill mentions a canonical role, use the corresponding label shown above. From 40849445bb6fdff4bc05c2f3c4937765fed1a9bb Mon Sep 17 00:00:00 2001 From: Brian Li Date: Wed, 23 Sep 2026 18:18:57 -0700 Subject: [PATCH 05/29] Add Matt Pocock engineering skills --- AGENTS.md | 4 + docs/agents/triage-labels.md | 13 ++ skills/ask-matt/PHASE-BOUNDARIES.md | 55 +++++ skills/ask-matt/SKILL.md | 90 ++++++++ skills/ask-matt/agents/openai.yaml | 6 + skills/claude-handoff/SKILL.md | 18 ++ skills/claude-handoff/agents/openai.yaml | 6 + skills/code-review/SKILL.md | 87 ++++++++ skills/code-review/agents/openai.yaml | 3 + skills/codebase-design/DEEPENING.md | 37 ++++ skills/codebase-design/DESIGN-IT-TWICE.md | 44 ++++ skills/codebase-design/SKILL.md | 114 ++++++++++ skills/codebase-design/agents/openai.yaml | 3 + skills/diagnosing-bugs/SKILL.md | 138 ++++++++++++ skills/diagnosing-bugs/agents/openai.yaml | 3 + .../scripts/hitl-loop.template.sh | 44 ++++ skills/domain-modeling/ADR-FORMAT.md | 47 ++++ skills/domain-modeling/CONTEXT-FORMAT.md | 60 +++++ skills/domain-modeling/SKILL.md | 74 +++++++ skills/domain-modeling/agents/openai.yaml | 3 + skills/git-guardrails-claude-code/SKILL.md | 95 ++++++++ .../agents/openai.yaml | 3 + .../scripts/block-dangerous-git.sh | 25 +++ skills/grill-me/SKILL.md | 7 + skills/grill-me/agents/openai.yaml | 6 + skills/grill-with-docs/SKILL.md | 7 + skills/grill-with-docs/agents/openai.yaml | 6 + skills/grilling/SKILL.md | 28 +++ skills/grilling/agents/openai.yaml | 3 + skills/handoff/SKILL.md | 16 ++ skills/handoff/agents/openai.yaml | 6 + skills/implement-spec/SKILL.md | 35 +++ skills/implement-spec/agents/openai.yaml | 6 + skills/implement/SKILL.md | 15 ++ skills/implement/agents/openai.yaml | 6 + .../HTML-REPORT.md | 123 +++++++++++ skills/improve-codebase-architecture/SKILL.md | 71 ++++++ .../agents/openai.yaml | 6 + skills/loop-me/SKILL.md | 32 +++ skills/loop-me/agents/openai.yaml | 6 + skills/migrate-to-shoehorn/SKILL.md | 118 ++++++++++ skills/migrate-to-shoehorn/agents/openai.yaml | 3 + skills/prototype/LOGIC.md | 67 ++++++ skills/prototype/SKILL.md | 26 +++ skills/prototype/UI.md | 112 ++++++++++ skills/prototype/agents/openai.yaml | 3 + skills/research/SKILL.md | 12 + skills/research/agents/openai.yaml | 3 + skills/resolving-merge-conflicts/SKILL.md | 14 ++ .../agents/openai.yaml | 3 + skills/retro/SKILL.md | 44 ++++ skills/retro/agents/openai.yaml | 6 + skills/scaffold-exercises/SKILL.md | 106 +++++++++ skills/scaffold-exercises/agents/openai.yaml | 3 + skills/setup-matt-pocock-skills/SKILL.md | 116 ++++++++++ .../agents/openai.yaml | 6 + skills/setup-matt-pocock-skills/domain.md | 51 +++++ .../issue-tracker-github.md | 45 ++++ .../issue-tracker-gitlab.md | 46 ++++ .../issue-tracker-local.md | 30 +++ .../setup-matt-pocock-skills/triage-labels.md | 15 ++ skills/setup-pre-commit/SKILL.md | 91 ++++++++ skills/setup-pre-commit/agents/openai.yaml | 3 + skills/setup-ts-deep-modules/SKILL.md | 102 +++++++++ .../setup-ts-deep-modules/agents/openai.yaml | 6 + .../dependency-cruiser.config.cjs | 95 ++++++++ skills/tdd/SKILL.md | 38 ++++ skills/tdd/agents/openai.yaml | 3 + skills/tdd/mocking.md | 59 +++++ skills/tdd/tests.md | 77 +++++++ skills/teach/GLOSSARY-FORMAT.md | 35 +++ skills/teach/LEARNING-RECORD-FORMAT.md | 46 ++++ skills/teach/MISSION-FORMAT.md | 31 +++ skills/teach/RESOURCES-FORMAT.md | 32 +++ skills/teach/SKILL.md | 140 ++++++++++++ skills/teach/agents/openai.yaml | 6 + skills/to-questionnaire/SKILL.md | 54 +++++ skills/to-questionnaire/agents/openai.yaml | 6 + skills/to-spec/SKILL.md | 75 +++++++ skills/to-spec/agents/openai.yaml | 6 + skills/to-tickets/SKILL.md | 105 +++++++++ skills/to-tickets/agents/openai.yaml | 6 + skills/triage/AGENT-BRIEF.md | 207 ++++++++++++++++++ skills/triage/OUT-OF-SCOPE.md | 105 +++++++++ skills/triage/SKILL.md | 112 ++++++++++ skills/triage/agents/openai.yaml | 6 + skills/wait-what/SKILL.md | 7 + skills/wait-what/agents/openai.yaml | 6 + skills/wayfinder/SKILL.md | 128 +++++++++++ skills/wayfinder/agents/openai.yaml | 6 + skills/wizard/SKILL.md | 44 ++++ skills/wizard/agents/openai.yaml | 3 + skills/wizard/template.sh | 204 +++++++++++++++++ skills/writing-beats/SKILL.md | 67 ++++++ skills/writing-beats/agents/openai.yaml | 6 + skills/writing-for-agents/SKILL-MECHANICS.md | 22 ++ skills/writing-for-agents/SKILL.md | 81 +++++++ skills/writing-for-agents/agents/openai.yaml | 3 + skills/writing-fragments/SKILL.md | 79 +++++++ skills/writing-fragments/agents/openai.yaml | 6 + skills/writing-shape/SKILL.md | 79 +++++++ skills/writing-shape/agents/openai.yaml | 6 + 102 files changed, 4373 insertions(+) create mode 100644 docs/agents/triage-labels.md create mode 100644 skills/ask-matt/PHASE-BOUNDARIES.md create mode 100644 skills/ask-matt/SKILL.md create mode 100644 skills/ask-matt/agents/openai.yaml create mode 100644 skills/claude-handoff/SKILL.md create mode 100644 skills/claude-handoff/agents/openai.yaml create mode 100644 skills/code-review/SKILL.md create mode 100644 skills/code-review/agents/openai.yaml create mode 100644 skills/codebase-design/DEEPENING.md create mode 100644 skills/codebase-design/DESIGN-IT-TWICE.md create mode 100644 skills/codebase-design/SKILL.md create mode 100644 skills/codebase-design/agents/openai.yaml create mode 100644 skills/diagnosing-bugs/SKILL.md create mode 100644 skills/diagnosing-bugs/agents/openai.yaml create mode 100644 skills/diagnosing-bugs/scripts/hitl-loop.template.sh create mode 100644 skills/domain-modeling/ADR-FORMAT.md create mode 100644 skills/domain-modeling/CONTEXT-FORMAT.md create mode 100644 skills/domain-modeling/SKILL.md create mode 100644 skills/domain-modeling/agents/openai.yaml create mode 100644 skills/git-guardrails-claude-code/SKILL.md create mode 100644 skills/git-guardrails-claude-code/agents/openai.yaml create mode 100644 skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh create mode 100644 skills/grill-me/SKILL.md create mode 100644 skills/grill-me/agents/openai.yaml create mode 100644 skills/grill-with-docs/SKILL.md create mode 100644 skills/grill-with-docs/agents/openai.yaml create mode 100644 skills/grilling/SKILL.md create mode 100644 skills/grilling/agents/openai.yaml create mode 100644 skills/handoff/SKILL.md create mode 100644 skills/handoff/agents/openai.yaml create mode 100644 skills/implement-spec/SKILL.md create mode 100644 skills/implement-spec/agents/openai.yaml create mode 100644 skills/implement/SKILL.md create mode 100644 skills/implement/agents/openai.yaml create mode 100644 skills/improve-codebase-architecture/HTML-REPORT.md create mode 100644 skills/improve-codebase-architecture/SKILL.md create mode 100644 skills/improve-codebase-architecture/agents/openai.yaml create mode 100644 skills/loop-me/SKILL.md create mode 100644 skills/loop-me/agents/openai.yaml create mode 100644 skills/migrate-to-shoehorn/SKILL.md create mode 100644 skills/migrate-to-shoehorn/agents/openai.yaml create mode 100644 skills/prototype/LOGIC.md create mode 100644 skills/prototype/SKILL.md create mode 100644 skills/prototype/UI.md create mode 100644 skills/prototype/agents/openai.yaml create mode 100644 skills/research/SKILL.md create mode 100644 skills/research/agents/openai.yaml create mode 100644 skills/resolving-merge-conflicts/SKILL.md create mode 100644 skills/resolving-merge-conflicts/agents/openai.yaml create mode 100644 skills/retro/SKILL.md create mode 100644 skills/retro/agents/openai.yaml create mode 100644 skills/scaffold-exercises/SKILL.md create mode 100644 skills/scaffold-exercises/agents/openai.yaml create mode 100644 skills/setup-matt-pocock-skills/SKILL.md create mode 100644 skills/setup-matt-pocock-skills/agents/openai.yaml create mode 100644 skills/setup-matt-pocock-skills/domain.md create mode 100644 skills/setup-matt-pocock-skills/issue-tracker-github.md create mode 100644 skills/setup-matt-pocock-skills/issue-tracker-gitlab.md create mode 100644 skills/setup-matt-pocock-skills/issue-tracker-local.md create mode 100644 skills/setup-matt-pocock-skills/triage-labels.md create mode 100644 skills/setup-pre-commit/SKILL.md create mode 100644 skills/setup-pre-commit/agents/openai.yaml create mode 100644 skills/setup-ts-deep-modules/SKILL.md create mode 100644 skills/setup-ts-deep-modules/agents/openai.yaml create mode 100644 skills/setup-ts-deep-modules/dependency-cruiser.config.cjs create mode 100644 skills/tdd/SKILL.md create mode 100644 skills/tdd/agents/openai.yaml create mode 100644 skills/tdd/mocking.md create mode 100644 skills/tdd/tests.md create mode 100644 skills/teach/GLOSSARY-FORMAT.md create mode 100644 skills/teach/LEARNING-RECORD-FORMAT.md create mode 100644 skills/teach/MISSION-FORMAT.md create mode 100644 skills/teach/RESOURCES-FORMAT.md create mode 100644 skills/teach/SKILL.md create mode 100644 skills/teach/agents/openai.yaml create mode 100644 skills/to-questionnaire/SKILL.md create mode 100644 skills/to-questionnaire/agents/openai.yaml create mode 100644 skills/to-spec/SKILL.md create mode 100644 skills/to-spec/agents/openai.yaml create mode 100644 skills/to-tickets/SKILL.md create mode 100644 skills/to-tickets/agents/openai.yaml create mode 100644 skills/triage/AGENT-BRIEF.md create mode 100644 skills/triage/OUT-OF-SCOPE.md create mode 100644 skills/triage/SKILL.md create mode 100644 skills/triage/agents/openai.yaml create mode 100644 skills/wait-what/SKILL.md create mode 100644 skills/wait-what/agents/openai.yaml create mode 100644 skills/wayfinder/SKILL.md create mode 100644 skills/wayfinder/agents/openai.yaml create mode 100644 skills/wizard/SKILL.md create mode 100644 skills/wizard/agents/openai.yaml create mode 100644 skills/wizard/template.sh create mode 100644 skills/writing-beats/SKILL.md create mode 100644 skills/writing-beats/agents/openai.yaml create mode 100644 skills/writing-for-agents/SKILL-MECHANICS.md create mode 100644 skills/writing-for-agents/SKILL.md create mode 100644 skills/writing-for-agents/agents/openai.yaml create mode 100644 skills/writing-fragments/SKILL.md create mode 100644 skills/writing-fragments/agents/openai.yaml create mode 100644 skills/writing-shape/SKILL.md create mode 100644 skills/writing-shape/agents/openai.yaml diff --git a/AGENTS.md b/AGENTS.md index 4b28c30..44ee6bb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,10 @@ Issues and specs are tracked in GitHub Issues for `BrianLiDesign/coffee-shop`. See `docs/agents/issue-tracker.md`. +### Triage labels + +The default triage labels are used: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, and `wontfix`. See `docs/agents/triage-labels.md`. + ### Domain docs This is a single-context repository using a root `CONTEXT.md` and `docs/adr/`. See `docs/agents/domain.md`. diff --git a/docs/agents/triage-labels.md b/docs/agents/triage-labels.md new file mode 100644 index 0000000..0806b2f --- /dev/null +++ b/docs/agents/triage-labels.md @@ -0,0 +1,13 @@ +# Triage Labels + +The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker. + +| Label in mattpocock/skills | Label in our tracker | Meaning | +| -------------------------- | -------------------- | ---------------------------------------- | +| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue | +| `needs-info` | `needs-info` | Waiting on reporter for more information | +| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent | +| `ready-for-human` | `ready-for-human` | Requires human implementation | +| `wontfix` | `wontfix` | Will not be actioned | + +When a skill mentions a role, use the corresponding label string from this table. diff --git a/skills/ask-matt/PHASE-BOUNDARIES.md b/skills/ask-matt/PHASE-BOUNDARIES.md new file mode 100644 index 0000000..fb58ef9 --- /dev/null +++ b/skills/ask-matt/PHASE-BOUNDARIES.md @@ -0,0 +1,55 @@ +# Phase boundaries + +A **phase** is a chunk of work inside a session: the grilling, the implementation, the QA. The definition is fuzzy on purpose: a phase ends when you think *"ok, we're done with that"*. + +The **phase boundary** is the gap between two phases, and it is the only place this decision belongs. Mid-phase there is no decision to make: continue, or split the work that's left into subagents. Compacting mid-phase makes the agent lose the thread. + +## The five options + +| Option | What it does | +| ------------ | --------------------------------------------------------------- | +| **Continue** | Stay in the session. No context switch at all. | +| **`/clear`** | Empty the context window and start from nothing. | +| **`/handoff`** | Write a portable markdown file and seed a session anywhere with it. | +| **Subagent** | Send the task to its own context window and get a report back. | +| **`/compact`** | Compress this context and seed a fresh session with the summary. | + +## The tree + +Work top to bottom at the boundary. The first **yes** wins. + +**1. Can you continue in this session?** Two things make the answer yes: the next phase needs this phase as a **primary source**, or you have enough [smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone) left (~150k tokens) for the next phase to fit. Grilling → implementation is the standard yes: the implementation wants the reasoning verbatim, not a summary of it. Continue costs nothing and loses nothing, so rule it out before anything else. + +**2. Is the context irrelevant to what comes next?** Is everything in this session (the exploration, the decisions, the dead ends) disposable? If so, **`/clear`**. It is the cheapest move on the board: it takes no time and hands back the whole window. `/clear` also isn't terminal: the old session stays resumable. + +The cost of getting this wrong is one-way. Clear a *relevant* context and you lose the **why** behind what you built, and no amount of reading the diff back gets it returned. + +**3. Do you need to hand off?** `/handoff` is narrow. You need it only when you are: + +- swapping to a **new harness** (Claude → Codex), +- moving to a **new directory** or repo, +- sending the work to a **colleague**, +- or forking a side task you found **mid-phase** without derailing what you're doing. + +That list is the whole clause. What `/handoff` buys is **portability**: a file that travels. If nothing is travelling, you don't need it. + +**4. Can the task be done AFK?** Is it scoped tightly enough to run with you away from the keyboard, no steering? Then send it to a **subagent** and leave this session untouched. Automated review is the standard case: the agent reads the diff and reports, and you aren't needed while it does. + +**5. Otherwise, `/compact`.** Relevant context, same harness, same directory, and you need to stay in the loop: this is where the tree lands, and it lands here often. Pass it an instruction (`/compact we're going to QA this area`) so the summary keeps what the next phase needs. + +`/compact` is the **default, not the first reach**. It sits at the bottom because the four questions above it are all cheaper or more precise. The failure mode when people start here is a fresh session that is confidently wrong about a decision the summary flattened. + +## Primary and secondary sources + +Every move except **Continue** turns a **primary source** into a **secondary source**: the session as it happened, replaced by a summary of it. The trade is always the same shape: + +| Source | Information | Noise | Room to move | +| --------------------------------- | ----------- | ----- | ------------ | +| Primary (Continue) | Full | Lots | Little | +| Secondary (`/compact`, `/handoff`) | Lossy | Less | Lots | + +This is why question 1 comes first. You only pay the lossiness when staying costs more than it saves. + +## These are judgement calls + +The questions are not objective: each has taste in it, and the same boundary can go two ways on two days. The value is in asking them **in order**, at the boundary rather than in the middle of the work. diff --git a/skills/ask-matt/SKILL.md b/skills/ask-matt/SKILL.md new file mode 100644 index 0000000..d2f8fc0 --- /dev/null +++ b/skills/ask-matt/SKILL.md @@ -0,0 +1,90 @@ +--- +name: ask-matt +description: Ask which skill or flow fits your situation. A router over the skills in this repo. +disable-model-invocation: false +--- + +# Ask Matt + +You don't remember every skill, so ask. + +A **flow** is a path through the skills. Most paths run along one **main flow**, and two **on-ramps** merge onto it. Everything else is standalone, or a vocabulary layer that runs underneath. + +## The main flow: idea → ship + +The route most work travels. You have an idea and want it built. + +1. **`/grill-with-docs`** sharpens the idea by interview. Start here whenever you are **working in a working directory**: it's stateful, retaining what it learns in `CONTEXT.md` and ADRs. (No working directory? Use `/grill-me` instead, covered under Standalone. Both run the same `/grilling` primitive; `grill-with-docs` is the one that leaves a paper trail, which makes it the better of the two whenever a repo is there to leave it in.) +2. **Branch: can you settle every question in conversation?** If a question needs a runnable answer (state, business logic, a UI you have to see), detour through a prototype, bridged by **`/handoff`** in both directions (a prototype lives in its own directory, which is exactly what `/handoff` is for; see Phase boundaries): + - **`/handoff`** out, then open a fresh session against that file, + - **`/prototype`** to answer the question with throwaway code, + - **`/handoff`** back what you learned, and reference it from the original idea thread. +3. **Branch: is this a multi-session build?** + - **Yes** → **`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**. On a local tracker that's one file per ticket under `.scratch//issues/`, worked blockers-first by hand; on a real tracker the edges become native blocking links, so any ticket whose blockers are done can be grabbed: kick off **`/implement`** per ticket, **`/clear`ing context between each one**. Each ticket is self-contained, so the last one's context is disposable. + - **No** → **`/implement`** right here, in the same context window. + + Either way, **`/implement`** builds each issue by driving **`/tdd`** internally (one red-green slice at a time), then closes out by running **`/code-review`**, a two-axis review (Standards + Spec) of the diff, before committing. Reach for **`/tdd`** on its own when you just want to build a concrete behaviour test-first without a full spec, and **`/code-review`** on its own whenever you want to review a branch or PR against a fixed point. + +### Context hygiene + +Keep steps 1–3 in **one unbroken context window** (don't compact or clear until after `/to-tickets`) so the grilling, spec, and tickets all build on the same thinking. Each `/implement` then starts fresh, working from the ticket. + +The limit on this is the **[smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone)**: the window (~150k tokens on state-of-the-art models) within which the model still reasons sharply. If a session approaches it before `/to-tickets`, don't push on degraded; `/compact` at the nearest phase boundary and carry on (see Phase boundaries). + +## On-ramps + +A starting situation that generates work, then merges onto the main flow. + +- **Bugs and requests piling up** → **`/triage`**. It moves issues through triage roles and produces agent-ready issues, which **`/implement`** later picks up. + + Triage is only for issues **you didn't create**: bug reports, incoming feature requests, anything that arrives raw. Tickets that `/to-tickets` produced are already agent-ready, so **don't triage them**. + +- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** (one command that already goes red on *this* bug), then fixes with a regression test. Its post-mortem hands off to **`/improve-codebase-architecture`** when the real finding is that there's no good seam to lock the bug down. + +- **A huge, foggy effort: a greenfield project or a huge feature build, too big for one session** → **`/wayfinder`**, the most cognitively demanding flow here. When the way from here to the destination isn't visible yet, it charts a **shared map** of **decision tickets** on the issue tracker and resolves them one at a time, producing **decisions, not deliverables**, until the fog is pushed back and the way is clear. Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't, and it's slower and denser, so save it for exactly that, never a well-scoped feature. + + When the map clears, **it hands off, it doesn't build**: merge onto the main flow at **`/to-spec`**, which collapses the map's linked decisions into a buildable plan, then `/to-tickets` and `/implement` as usual. Looping the map straight into `/implement` skips that collapse and throws the linked detail away, so go straight to `/implement` only when the effort turned out genuinely small. + +## Codebase health + +Not feature work, just upkeep. + +- **`/improve-codebase-architecture`** runs whenever you have a spare moment to keep the codebase good for agents to operate in. It surfaces **deepening opportunities**; picking one _generates an idea_ you can take into the main flow at `/grill-with-docs`. It's the survey that finds the candidates; **`/codebase-design`** (below) is the bench you design the chosen one on. + +## Vocabulary underneath + +Two model-invoked references that run *beneath* the other skills, each the single source of truth for its vocabulary. Reach for them directly when the **words**, not the process, are the problem; or let the skills above pull them in. + +- **`/domain-modeling`**: sharpen the project's *domain* language: challenge a fuzzy term, resolve an overloaded word ("account" doing three jobs), record a hard-to-reverse decision as an ADR. It's the active discipline `/grill-with-docs` drives to keep `CONTEXT.md` a clean glossary. +- **`/codebase-design`** is the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality) for designing a module's *shape*: a lot of behaviour behind a small interface at a clean seam. `/tdd` and `/improve-codebase-architecture` both speak it. + +## Phase boundaries + +A **phase** is a chunk of work inside a session: the grilling, the implementation, the QA. At the **boundary** between two of them you have five options, and picking between them is the fuzziest decision in this whole map: + +- **Continue**: stay put. Costs nothing, loses nothing. +- **`/clear`**: empty the window, when nothing here matters to what's next. +- **`/handoff`** writes a portable markdown file. Narrow: only for a **new harness**, a **new directory**, a **colleague**, or forking a side task **mid-phase**. What it buys is portability. +- **Subagent**: send a tightly-scoped task to its own window and get a report back. +- **`/compact`** compresses this context and seeds a fresh session with it. The **default**, at the bottom of the tree rather than the first reach. + +Read [PHASE-BOUNDARIES.md](PHASE-BOUNDARIES.md) for the ordered tree: the five questions, the reasoning behind each branch, and why the primary-source cost makes **Continue** the one to rule out first. Make the decision **at** a boundary; mid-phase, continue or split the rest into subagents. + +## Standalone + +Off the main flow entirely. + +- **`/grill-me`**: the same relentless interview as `/grill-with-docs`, but **stateless**: it saves nothing locally and builds no `CONTEXT.md`. Reach for it when you are **not working in a working directory** (sharpening a plan, a design, a piece of writing, anything with no repo under it). If you are in a working directory, use `/grill-with-docs` instead: it runs the same interview and leaves a paper trail, so it is strictly the better one. +- **`/grilling`** is the interview primitive itself: rounds, the frontier, facts are the agent's job and decisions are yours. `/grill-me` and `/grill-with-docs` are the two named ways in, and `/triage`, `/wayfinder` and `/improve-codebase-architecture` all run it internally. Reach for it directly only when you want the interview with no wrapper around it. +- **`/resolving-merge-conflicts`** works an in-progress merge or rebase conflict hunk by hunk, resolving by **intent** traced to each side's primary source rather than by picking lines, then finishes the operation. It never runs `--abort`. Standalone and off every flow: reach for it when you are already mid-conflict. +- **`/prototype`** is a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway is a constraint on how the code is written, not a promise to destroy it: the answer folds into the real code, and the prototype itself is kept as a **primary source** on a `prototype/` branch out of main, pointed at from the implementation issue. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper. +- **`/research`**: delegate reading legwork to a **background agent**: it investigates a question against **primary sources**, then leaves a cited Markdown file in the repo. Keep working while it reads. The file it produces is something to take *into* the main flow at `/grill-with-docs`, since research feeds the thinking rather than replacing it. +- **`/to-questionnaire`** comes in when the thing blocking you isn't in your head or the codebase but in **someone else's**, and it writes them a questionnaire to fill in. It's the inverse of `/grill-me`: instead of interviewing you about the subject, it interviews you about the **send** (who it's going to, what you need back) and aims the questions at the gap. What comes back is material for `/grill-with-docs` or `/to-spec`. +- **`/wizard`** is for the steps only a **human** can take: provisioning infrastructure, setting up credentials or CI secrets, clicking through an unfamiliar third-party dashboard, running a one-off migration or cutover. It generates an interactive bash script that opens each URL, captures each value, and writes it into `.env` and GitHub secrets, so the procedure stops being something you re-explain to an agent every time. Model-invoked, so the agent reaches for it the moment it hits a wall only you can pass. If the agent could just do it itself, it should; this is for where a human is genuinely in the loop. +- **`/wait-what`** is the corrective for a message that didn't land. Use it mid-conversation, inside any other skill, and the agent re-pitches what it just said with the context you were missing, in plain English, using the `CONTEXT.md` vocabulary. It works after the fact; `/grill-with-docs` is the upfront cure, because a shared language agreed early is what stops the jargon arriving at all. +- **`/teach`**: learn a concept over multiple sessions, using the current directory as a stateful workspace. +- **`/writing-for-agents`** is the reference for writing documents agents consume: skills, AGENTS.md, pointed-at docs. + +## Precondition + +**`/setup-matt-pocock-skills`**: run before your first engineering flow to configure the issue tracker, triage labels, and doc layout the other skills assume. Custom issue trackers also work. diff --git a/skills/ask-matt/agents/openai.yaml b/skills/ask-matt/agents/openai.yaml new file mode 100644 index 0000000..07d9218 --- /dev/null +++ b/skills/ask-matt/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Ask Matt" + short_description: "Explicitly invoke the Ask Matt workflow" + default_prompt: "Use $ask-matt to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/claude-handoff/SKILL.md b/skills/claude-handoff/SKILL.md new file mode 100644 index 0000000..da428ce --- /dev/null +++ b/skills/claude-handoff/SKILL.md @@ -0,0 +1,18 @@ +--- +name: claude-handoff +description: Hand the current conversation off to a fresh background agent that picks up the work immediately. +argument-hint: "What will the next session be used for?" +disable-model-invocation: false +--- + +Write a handoff summary of the current conversation so a fresh agent can continue the work. Instead of saving it, launch a background agent seeded with the summary as its prompt: `claude --bg --name "" ""`. It starts in the current working directory and returns immediately; the user manages it with `claude agents`. + +Always pass `-n`/`--name` with a descriptive name (e.g. `--name "Fix login bug"`); it sets the display name shown in the job list, session picker, and terminal title. + +Include a "suggested skills" section in the summary, naming which skills the next agent should call the Skill tool for. + +Do not duplicate content already captured in other artifacts (specs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead. + +Redact any sensitive information, such as API keys, passwords, or personally identifiable information, since the summary becomes the agent's prompt. + +If the user passed arguments, treat them as a description of what the next session will focus on and tailor the summary accordingly. diff --git a/skills/claude-handoff/agents/openai.yaml b/skills/claude-handoff/agents/openai.yaml new file mode 100644 index 0000000..433bbaf --- /dev/null +++ b/skills/claude-handoff/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Claude Handoff" + short_description: "Explicitly invoke the Claude Handoff workflow" + default_prompt: "Use $claude-handoff to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md new file mode 100644 index 0000000..e28d7ac --- /dev/null +++ b/skills/code-review/SKILL.md @@ -0,0 +1,87 @@ +--- +name: code-review +description: "Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes: Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/spec asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to \"review since X\"." +--- + +Two-axis review of the diff between `HEAD` and a fixed point the user supplies: + +- **Standards**: does the code conform to this repo's documented coding standards? +- **Spec**: does the code faithfully implement the originating issue / spec? + +Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings. + +The issue tracker should have been provided to you. If `docs/agents/issue-tracker.md` is missing, tell the user to run `/setup-matt-pocock-skills`. + +## Process + +### 1. Pin the fixed point + +Whatever the user said is the fixed point (a commit SHA, branch name, tag, `main`, `HEAD~5`, etc.). If they didn't specify one, ask for it. + +Capture the diff command once: `git diff ...HEAD` (three-dot, so the comparison is against the merge-base). Also note the list of commits via `git log ..HEAD --oneline`. + +Before going further, confirm the fixed point resolves (`git rev-parse `) and the diff is non-empty. A bad ref or empty diff should fail here, not inside two parallel sub-agents. + +### 2. Identify the spec source + +Look for the originating spec, in this order: + +1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.), fetched via the workflow in `docs/agents/issue-tracker.md`. +2. A path the user passed as an argument. +3. A spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature. +4. If nothing is found, ask the user where the spec is. If they say there isn't one, the **Spec** sub-agent will skip and report "no spec available". + +### 3. Identify the standards sources + +Anything in the repo that documents how code should be written, such as `CODING_STANDARDS.md` or `CONTRIBUTING.md`. + +On top of whatever the repo documents, the Standards axis always carries the **smell baseline** below: a fixed set of Fowler code smells (_Refactoring_, ch.3) that applies even when a repo documents nothing. Two rules bind it: + +- **The repo overrides.** A documented repo standard always wins; where it endorses something the baseline would flag, suppress the smell. +- **Always a judgement call.** Each smell is a labelled heuristic ("possible Feature Envy"), never a hard violation. Like any standard here, skip anything tooling already enforces. + +Each smell reads *what it is* → *how to fix*; match it against the diff: + +- **Mysterious Name**: a function, variable, or type whose name doesn't reveal what it does or holds. → rename it; if no honest name comes, the design's murky. +- **Duplicated Code**: the same logic shape appears in more than one hunk or file in the change. → extract the shared shape, call it from both. +- **Feature Envy**: a method that reaches into another object's data more than its own. → move the method onto the data it envies. +- **Data Clumps**: the same few fields or params keep travelling together (a type wanting to be born). → bundle them into one type, pass that. +- **Primitive Obsession**: a primitive or string standing in for a domain concept that deserves its own type. → give the concept its own small type. +- **Repeated Switches**: the same `switch`/`if`-cascade on the same type recurs across the change. → replace with polymorphism, or one map both sites share. +- **Shotgun Surgery**: one logical change forces scattered edits across many files in the diff. → gather what changes together into one module. +- **Divergent Change**: one file or module is edited for several unrelated reasons. → split so each module changes for one reason. +- **Speculative Generality**: abstraction, parameters, or hooks added for needs the spec doesn't have. → delete it; inline back until a real need shows. +- **Message Chains**: long `a.b().c().d()` navigation the caller shouldn't depend on. → hide the walk behind one method on the first object. +- **Middle Man**: a class or function that mostly just delegates onward. → cut it, call the real target direct. +- **Refused Bequest**: a subclass or implementer that ignores or overrides most of what it inherits. → drop the inheritance, use composition. + +### 4. Spawn both sub-agents in parallel + +**Standards sub-agent prompt** should include: + +- The full diff command and commit list. +- The list of standards-source files you found in step 3, **plus the smell baseline from step 3** pasted in full (the sub-agent has no other access to it). +- The brief: "Report, per file/hunk where relevant, (a) every place the diff violates a documented standard: cite the standard (file + the rule); and (b) any baseline smell you spot: name it and quote the hunk. Distinguish hard violations from judgement calls: documented-standard breaches can be hard, but baseline smells are always judgement calls, and a documented repo standard overrides the baseline. Skip anything tooling enforces. Under 400 words." + +**Spec sub-agent prompt** should include: + +- The diff command and commit list. +- The path or fetched contents of the spec. +- The brief: "Report: (a) requirements the spec asked for that are missing or partial; (b) behaviour in the diff that wasn't asked for (scope creep); (c) requirements that look implemented but where the implementation looks wrong. Quote the spec line for each finding. Under 400 words." + +If the spec is missing, skip the Spec sub-agent and note this in the final report. + +### 5. Aggregate + +Present the two reports under `## Standards` and `## Spec` headings, verbatim or lightly cleaned. Do **not** merge or rerank findings, because the two axes are deliberately separate (see _Why two axes_). + +End with a one-line summary: total findings per axis, and the worst issue _within each axis_ (if any). Don't pick a single winner across axes: that's the reranking the separation exists to prevent. + +## Why two axes + +A change can pass one axis and fail the other: + +- Code that follows every standard but implements the wrong thing → **Standards pass, Spec fail.** +- Code that does exactly what the issue asked but breaks the project's conventions → **Spec pass, Standards fail.** + +Reporting them separately stops one axis from masking the other. diff --git a/skills/code-review/agents/openai.yaml b/skills/code-review/agents/openai.yaml new file mode 100644 index 0000000..9076774 --- /dev/null +++ b/skills/code-review/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Code Review" + short_description: "Review a diff on standards and spec" diff --git a/skills/codebase-design/DEEPENING.md b/skills/codebase-design/DEEPENING.md new file mode 100644 index 0000000..cd94075 --- /dev/null +++ b/skills/codebase-design/DEEPENING.md @@ -0,0 +1,37 @@ +# Deepening + +How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](SKILL.md): **module**, **interface**, **seam**, **adapter**. + +## Dependency categories + +When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam. + +### 1. In-process + +Pure computation, in-memory state, no I/O. Always deepenable: merge the modules and test through the new interface directly. No adapter needed. + +### 2. Local-substitutable + +Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface. + +### 3. Remote but owned (Ports & Adapters) + +Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter. + +Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."* + +### 4. True external (Mock) + +Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter. + +## Seam discipline + +- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection. +- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them. + +## Testing strategy: replace, don't layer + +- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist; delete them. +- Write new tests at the deepened module's interface. The **interface is the test surface**. +- Tests assert on observable outcomes through the interface, not internal state. +- Tests should survive internal refactors, since they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface. diff --git a/skills/codebase-design/DESIGN-IT-TWICE.md b/skills/codebase-design/DESIGN-IT-TWICE.md new file mode 100644 index 0000000..7edc861 --- /dev/null +++ b/skills/codebase-design/DESIGN-IT-TWICE.md @@ -0,0 +1,44 @@ +# Design It Twice + +When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout): your first idea is unlikely to be the best. + +Uses the vocabulary in [SKILL.md](SKILL.md): **module**, **interface**, **seam**, **adapter**, **leverage**. + +## Process + +### 1. Frame the problem space + +Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate: + +- The constraints any new interface would need to satisfy +- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md)) +- A rough illustrative code sketch to ground the constraints, not a proposal, just a way to make the constraints concrete + +Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel. + +### 2. Spawn sub-agents + +Spawn 3+ sub-agents in parallel. Each must produce a **radically different** interface for the deepened module. + +Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint: + +- Agent 1: "Minimize the interface: aim for 1–3 entry points max. Maximise leverage per entry point." +- Agent 2: "Maximise flexibility: support many use cases and extension." +- Agent 3: "Optimise for the most common caller: make the default case trivial." +- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies." + +Include both [SKILL.md](SKILL.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language. + +Each sub-agent outputs: + +1. Interface (types, methods, params, plus invariants, ordering, error modes) +2. Usage example showing how callers use it +3. What the implementation hides behind the seam +4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md)) +5. Trade-offs: where leverage is high, where it's thin + +### 3. Present and compare + +Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. + +After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated: the user wants a strong read, not a menu. diff --git a/skills/codebase-design/SKILL.md b/skills/codebase-design/SKILL.md new file mode 100644 index 0000000..3f63c81 --- /dev/null +++ b/skills/codebase-design/SKILL.md @@ -0,0 +1,114 @@ +--- +name: codebase-design +description: Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary. +--- + +# Codebase Design + +Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone. + +## Glossary + +Use these terms exactly: don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point. + +**Module**: anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service. + +**Interface**: everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow, they refer only to the type-level surface). + +**Implementation**: what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. + +**Depth**: leverage at the interface. The amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation. + +**Seam** _(Michael Feathers)_: a place where you can alter behaviour without editing in that place; the *location* at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context). + +**Adapter**: a concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside). + +**Leverage**: what callers get from depth. More capability per unit of interface they learn. One implementation pays back across N call sites and M tests. + +**Locality**: what maintainers get from depth. Change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere. + +## Deep vs shallow + +**Deep module** = small interface + lots of implementation: + +``` +┌─────────────────────┐ +│ Small Interface │ ← Few methods, simple params +├─────────────────────┤ +│ │ +│ Deep Implementation│ ← Complex logic hidden +│ │ +└─────────────────────┘ +``` + +**Shallow module** = large interface + little implementation (avoid): + +``` +┌─────────────────────────────────┐ +│ Large Interface │ ← Many methods, complex params +├─────────────────────────────────┤ +│ Thin Implementation │ ← Just passes through +└─────────────────────────────────┘ +``` + +When designing an interface, ask: + +- Can I reduce the number of methods? +- Can I simplify the parameters? +- Can I hide more complexity inside? + +## Principles + +- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts; they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface. +- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. +- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape. +- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. + +## Designing for testability + +Good interfaces make testing natural: + +1. **Accept dependencies, don't create them.** + + ```typescript + // Testable + function processOrder(order, paymentGateway) {} + + // Hard to test + function processOrder(order) { + const gateway = new StripeGateway(); + } + ``` + +2. **Return results, don't produce side effects.** + + ```typescript + // Testable + function calculateDiscount(cart): Discount {} + + // Hard to test + function applyDiscount(cart): void { + cart.total -= discount; + } + ``` + +3. **Small surface area.** Fewer methods = fewer tests needed. Fewer params = simpler test setup. + +## Relationships + +- A **Module** has exactly one **Interface** (the surface it presents to callers and tests). +- **Depth** is a property of a **Module**, measured against its **Interface**. +- A **Seam** is where a **Module**'s **Interface** lives. +- An **Adapter** sits at a **Seam** and satisfies the **Interface**. +- **Depth** produces **Leverage** for callers and **Locality** for maintainers. + +## Rejected framings + +- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead. +- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow: interface here includes every fact a caller must know. +- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**. + +## Going deeper + +- **Deepening a cluster given its dependencies**, see [DEEPENING.md](DEEPENING.md): dependency categories, seam discipline, and replace-don't-layer testing. +- **Exploring alternative interfaces**, see [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md): spin up parallel sub-agents to design the interface several radically different ways, then compare on depth, locality, and seam placement. diff --git a/skills/codebase-design/agents/openai.yaml b/skills/codebase-design/agents/openai.yaml new file mode 100644 index 0000000..3180715 --- /dev/null +++ b/skills/codebase-design/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Codebase Design" + short_description: "Vocabulary for deep-module design" diff --git a/skills/diagnosing-bugs/SKILL.md b/skills/diagnosing-bugs/SKILL.md new file mode 100644 index 0000000..061c25a --- /dev/null +++ b/skills/diagnosing-bugs/SKILL.md @@ -0,0 +1,138 @@ +--- +name: diagnosing-bugs +description: Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow. +--- + +# Diagnosing Bugs + +A discipline for hard bugs. Skip phases only when explicitly justified. + +When exploring the codebase, read `CONTEXT.md` (if it exists) to get a clear mental model of the relevant modules, and check ADRs in the area you're touching. + +## Redact + +This skill has you show commands, outputs and captured artifacts. **Redact every secret first**: write `` in its place. Build loops against env vars, so the credential stays in the environment rather than in what you show. Captured artifacts carry auth headers: quote only the lines that carry the signal. + +If the redacted output is not enough to diagnose the bug, say so and ask the user. + +## Phase 1: Build a feedback loop + +**This is the skill.** Everything else is mechanical. If you have a **tight** pass/fail signal for the bug (one that goes red on _this_ bug), you will find the cause; bisection, hypothesis-testing, and instrumentation all just consume it. If you don't have one, no amount of staring at code will save you. + +Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.** + +### Ways to construct one, in roughly this order + +1. **Failing test** at whatever seam reaches the bug: unit, integration, e2e. +2. **Curl / HTTP script** against a running dev server. +3. **CLI invocation** with a fixture input, diffing stdout against a known-good snapshot. +4. **Headless browser script** (Playwright / Puppeteer) that drives the UI and asserts on DOM/console/network. +5. **Replay a captured trace.** Save a real network request / payload / event log to disk; replay it through the code path in isolation. +6. **Throwaway harness.** Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call. +7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode. +8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it. +9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs. +10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you. + +Build the right feedback loop, and the bug is 90% fixed. + +### Tighten the loop + +Treat the loop as a product. Once you have _a_ loop, **tighten** it: + +- Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.) +- Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".) +- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.) + +A 30-second flaky loop is barely better than no loop; a 2-second deterministic one is tight, a debugging superpower. + +### Non-deterministic bugs + +The goal is not a clean repro but a **higher reproduction rate**. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not, so keep raising the rate until it's debuggable. + +### When you genuinely cannot build a loop + +Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a redacted captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop. + +### Completion criterion: a tight loop that goes red + +Phase 1 is done when the loop is **tight** and **red-capable**: you can name **one command** (a script path, a test invocation, a curl) that you have **already run at least once** (show the invocation and its output, redacted), and that is: + +- [ ] **Red-capable**: it drives the actual bug code path and asserts the **user's exact symptom**, so it can go red on this bug and green once fixed. Not "runs without erroring"; it must be able to _catch this specific bug_. +- [ ] **Deterministic**: same verdict every run (flaky bugs: a pinned, high reproduction rate, per above). +- [ ] **Fast**: seconds, not minutes. +- [ ] **Agent-runnable**: you can run it unattended; a human in the loop only via `scripts/hitl-loop.template.sh`. + +If you catch yourself reading code to build a theory before this command exists, **stop: jumping straight to a hypothesis is the exact failure this skill prevents.** No red-capable command, no Phase 2. + +## Phase 2: Reproduce + minimise + +Run the loop. Watch it go red as the bug appears. + +Confirm: + +- [ ] The loop produces the failure mode the **user** described, not a different failure that happens to be nearby. Wrong bug = wrong fix. +- [ ] The failure is reproducible across multiple runs (or, for non-deterministic bugs, reproducible at a high enough rate to debug against). +- [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it. + +### Minimise + +Once it's red, shrink the repro to the **smallest scenario that still goes red**. Cut inputs, callers, config, data, and steps **one at a time**, re-running the loop after each cut, and keep only what's load-bearing for the failure. + +Why bother: a minimal repro shrinks the hypothesis space in Phase 3 (fewer moving parts left to suspect) and becomes the clean regression test in Phase 5. + +Done when **every remaining element is load-bearing**: removing any one of them makes the loop go green. + +Do not proceed until you have reproduced **and** minimised. + +## Phase 3: Hypothesise + +Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea. + +Each hypothesis must be **falsifiable**: state the prediction it makes. + +> Format: "If is the cause, then will make the bug disappear / will make it worse." + +If you cannot state the prediction, the hypothesis is a vibe: discard or sharpen it. + +**Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it; proceed with your ranking if the user is AFK. + +## Phase 4: Instrument + +Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.** + +Tool preference: + +1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs. +2. **Targeted logs** at the boundaries that distinguish hypotheses. +3. Never "log everything and grep". + +**Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die. + +**Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second. + +## Phase 5: Fix + regression test + +Write the regression test **before the fix**, but only if there is a **correct seam** for it. + +A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence. + +**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase. + +If a correct seam exists: + +1. Turn the minimised repro into a failing test at that seam. +2. Watch it fail. +3. Apply the fix. +4. Watch it pass. +5. Re-run the Phase 1 feedback loop against the original (un-minimised) scenario. + +## Phase 6: Cleanup + +Required before declaring done: + +- [ ] Original repro no longer reproduces (re-run the Phase 1 loop) +- [ ] Regression test passes (or absence of seam is documented) +- [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix) +- [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location) +- [ ] The hypothesis that turned out correct is stated in the commit / PR message, so the next debugger learns diff --git a/skills/diagnosing-bugs/agents/openai.yaml b/skills/diagnosing-bugs/agents/openai.yaml new file mode 100644 index 0000000..a13a755 --- /dev/null +++ b/skills/diagnosing-bugs/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Diagnosing Bugs" + short_description: "Diagnose hard bugs and regressions" diff --git a/skills/diagnosing-bugs/scripts/hitl-loop.template.sh b/skills/diagnosing-bugs/scripts/hitl-loop.template.sh new file mode 100644 index 0000000..2431984 --- /dev/null +++ b/skills/diagnosing-bugs/scripts/hitl-loop.template.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Human-in-the-loop reproduction loop. +# Copy this file, edit the steps below, and run it. +# The agent runs the script; the user follows prompts in their terminal. +# +# Usage: +# bash hitl-loop.template.sh +# +# Two helpers: +# step "" → show instruction, wait for Enter +# capture VAR "" → show question, read response into VAR +# +# At the end, captured values are printed as KEY=VALUE for the agent to parse. +# +# `capture` prints its value back to the terminal, where the agent reads it, +# so capture observations, and leave signing in to the user as a `step`. + +set -euo pipefail + +step() { + printf '\n>>> %s\n' "$1" + read -r -p " [Enter when done] " _ +} + +capture() { + local var="$1" question="$2" answer + printf '\n>>> %s\n' "$question" + read -r -p " > " answer + printf -v "$var" '%s' "$answer" +} + +# --- edit below --------------------------------------------------------- + +step "Open the app at http://localhost:3000 and sign in." + +capture ERRORED "Click the 'Export' button. Did it throw an error? (y/n)" + +capture ERROR_MSG "Paste the error message (or 'none'):" + +# --- edit above --------------------------------------------------------- + +printf '\n--- Captured ---\n' +printf 'ERRORED=%s\n' "$ERRORED" +printf 'ERROR_MSG=%s\n' "$ERROR_MSG" diff --git a/skills/domain-modeling/ADR-FORMAT.md b/skills/domain-modeling/ADR-FORMAT.md new file mode 100644 index 0000000..d7e61f3 --- /dev/null +++ b/skills/domain-modeling/ADR-FORMAT.md @@ -0,0 +1,47 @@ +# ADR Format + +ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc. + +Create the `docs/adr/` directory lazily: only when the first ADR is needed. + +## Template + +```md +# {Short title of the decision} + +{1-3 sentences: what's the context, what did we decide, and why.} +``` + +That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why*, not in filling out sections. + +## Optional sections + +Only include these when they add genuine value. Most ADRs won't need them. + +- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`): useful when decisions are revisited +- **Considered Options**: only when the rejected alternatives are worth remembering +- **Consequences**: only when non-obvious downstream effects need to be called out + +## Numbering + +Scan `docs/adr/` for the highest existing number and increment by one. + +## When to offer an ADR + +All three of these must be true: + +1. **Hard to reverse**: the cost of changing your mind later is meaningful +2. **Surprising without context**: a future reader will look at the code and wonder "why on earth did they do it this way?" +3. **The result of a real trade-off**: there were genuine alternatives and you picked one for specific reasons + +If a decision is easy to reverse, skip it: you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing." + +### What qualifies + +- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres." +- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP." +- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library: just the ones that would take a quarter to swap out. +- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s. +- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate. +- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract." +- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it; otherwise someone will suggest GraphQL again in six months. diff --git a/skills/domain-modeling/CONTEXT-FORMAT.md b/skills/domain-modeling/CONTEXT-FORMAT.md new file mode 100644 index 0000000..79bbb32 --- /dev/null +++ b/skills/domain-modeling/CONTEXT-FORMAT.md @@ -0,0 +1,60 @@ +# CONTEXT.md Format + +## Structure + +```md +# {Context Name} + +{One or two sentence description of what this context is and why it exists.} + +## Language + +**Order**: +{A one or two sentence description of the term} +_Avoid_: Purchase, transaction + +**Invoice**: +A request for payment sent to a customer after delivery. +_Avoid_: Bill, payment request + +**Customer**: +A person or organization that places orders. +_Avoid_: Client, buyer, account +``` + +## Rules + +- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`. +- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does. +- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs. +- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine. + +## Single vs multi-context repos + +**Single context (most repos):** One `CONTEXT.md` at the repo root. + +**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other: + +```md +# Context Map + +## Contexts + +- [Ordering](./src/ordering/CONTEXT.md): receives and tracks customer orders +- [Billing](./src/billing/CONTEXT.md): generates invoices and processes payments +- [Fulfillment](./src/fulfillment/CONTEXT.md): manages warehouse picking and shipping + +## Relationships + +- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking +- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices +- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money` +``` + +The skill infers which structure applies: + +- If `CONTEXT-MAP.md` exists, read it to find contexts +- If only a root `CONTEXT.md` exists, single context +- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved + +When multiple contexts exist, infer which one the current topic relates to. If unclear, ask. diff --git a/skills/domain-modeling/SKILL.md b/skills/domain-modeling/SKILL.md new file mode 100644 index 0000000..9b97707 --- /dev/null +++ b/skills/domain-modeling/SKILL.md @@ -0,0 +1,74 @@ +--- +name: domain-modeling +description: Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a CONTEXT.md, or recording or editing an ADR. +--- + +# Domain Modeling + +Actively build and sharpen the project's domain model as you design. This is the *active* discipline: challenging terms, inventing edge-case scenarios, and writing the glossary and decisions down the moment they crystallise. (Merely *reading* `CONTEXT.md` for vocabulary is not this skill: that's a one-line habit any skill can do. This skill is for when you're changing the model, not just consuming it.) + +## File structure + +Most repos have a single context: + +``` +/ +├── CONTEXT.md +├── docs/ +│ └── adr/ +│ ├── 0001-event-sourced-orders.md +│ └── 0002-postgres-for-write-model.md +└── src/ +``` + +If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives: + +``` +/ +├── CONTEXT-MAP.md +├── docs/ +│ └── adr/ ← system-wide decisions +├── src/ +│ ├── ordering/ +│ │ ├── CONTEXT.md +│ │ └── docs/adr/ ← context-specific decisions +│ └── billing/ +│ ├── CONTEXT.md +│ └── docs/adr/ +``` + +Create files lazily: only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed. + +## During the session + +### Challenge against the glossary + +When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y. Which is it?" + +### Sharpen fuzzy language + +When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account': do you mean the Customer or the User? Those are different things." + +### Discuss concrete scenarios + +When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts. + +### Cross-reference with code + +When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible. Which is right?" + +### Update CONTEXT.md inline + +When a term is resolved, update `CONTEXT.md` right there. Don't batch these up: capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md). + +`CONTEXT.md` should be totally devoid of implementation details. Do not treat `CONTEXT.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else. + +### Offer ADRs sparingly + +Only offer to create an ADR when all three are true: + +1. **Hard to reverse**: the cost of changing your mind later is meaningful +2. **Surprising without context**: a future reader will wonder "why did they do it this way?" +3. **The result of a real trade-off**: there were genuine alternatives and you picked one for specific reasons + +If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md). diff --git a/skills/domain-modeling/agents/openai.yaml b/skills/domain-modeling/agents/openai.yaml new file mode 100644 index 0000000..7f1522d --- /dev/null +++ b/skills/domain-modeling/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Domain Modeling" + short_description: "Build and sharpen a domain model" diff --git a/skills/git-guardrails-claude-code/SKILL.md b/skills/git-guardrails-claude-code/SKILL.md new file mode 100644 index 0000000..58bcdd8 --- /dev/null +++ b/skills/git-guardrails-claude-code/SKILL.md @@ -0,0 +1,95 @@ +--- +name: git-guardrails-claude-code +description: Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code. +--- + +# Setup Git Guardrails + +Sets up a PreToolUse hook that intercepts and blocks dangerous git commands before Claude executes them. + +## What Gets Blocked + +- `git push` (all variants including `--force`) +- `git reset --hard` +- `git clean -f` / `git clean -fd` +- `git branch -D` +- `git checkout .` / `git restore .` + +When blocked, Claude sees a message telling it that it does not have authority to access these commands. + +## Steps + +### 1. Ask scope + +Ask the user: install for **this project only** (`.claude/settings.json`) or **all projects** (`~/.claude/settings.json`)? + +### 2. Copy the hook script + +The bundled script is at: [scripts/block-dangerous-git.sh](scripts/block-dangerous-git.sh) + +Copy it to the target location based on scope: + +- **Project**: `.claude/hooks/block-dangerous-git.sh` +- **Global**: `~/.claude/hooks/block-dangerous-git.sh` + +Make it executable with `chmod +x`. + +### 3. Add hook to settings + +Add to the appropriate settings file: + +**Project** (`.claude/settings.json`): + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous-git.sh" + } + ] + } + ] + } +} +``` + +**Global** (`~/.claude/settings.json`): + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "~/.claude/hooks/block-dangerous-git.sh" + } + ] + } + ] + } +} +``` + +If the settings file already exists, merge the hook into the existing `hooks.PreToolUse` array. Don't overwrite other settings. + +### 4. Ask about customization + +Ask if user wants to add or remove any patterns from the blocked list. Edit the copied script accordingly. + +### 5. Verify + +Run a quick test: + +```bash +echo '{"tool_input":{"command":"git push origin main"}}' | +``` + +Should exit with code 2 and print a BLOCKED message to stderr. diff --git a/skills/git-guardrails-claude-code/agents/openai.yaml b/skills/git-guardrails-claude-code/agents/openai.yaml new file mode 100644 index 0000000..3f5d756 --- /dev/null +++ b/skills/git-guardrails-claude-code/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Git Guardrails for Claude Code" + short_description: "Block dangerous git commands" diff --git a/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh b/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh new file mode 100644 index 0000000..c40b59c --- /dev/null +++ b/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +INPUT=$(cat) +COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command') + +DANGEROUS_PATTERNS=( + "git push" + "git reset --hard" + "git clean -fd" + "git clean -f" + "git branch -D" + "git checkout \." + "git restore \." + "push --force" + "reset --hard" +) + +for pattern in "${DANGEROUS_PATTERNS[@]}"; do + if echo "$COMMAND" | grep -qE "$pattern"; then + echo "BLOCKED: '$COMMAND' matches dangerous pattern '$pattern'. The user has prevented you from doing this." >&2 + exit 2 + fi +done + +exit 0 diff --git a/skills/grill-me/SKILL.md b/skills/grill-me/SKILL.md new file mode 100644 index 0000000..3c450aa --- /dev/null +++ b/skills/grill-me/SKILL.md @@ -0,0 +1,7 @@ +--- +name: grill-me +description: A relentless interview to sharpen a plan or design. +disable-model-invocation: false +--- + +Call the Skill tool with "grilling". diff --git a/skills/grill-me/agents/openai.yaml b/skills/grill-me/agents/openai.yaml new file mode 100644 index 0000000..85c90e1 --- /dev/null +++ b/skills/grill-me/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Grill Me" + short_description: "Explicitly invoke the Grill Me workflow" + default_prompt: "Use $grill-me to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/grill-with-docs/SKILL.md b/skills/grill-with-docs/SKILL.md new file mode 100644 index 0000000..bab702b --- /dev/null +++ b/skills/grill-with-docs/SKILL.md @@ -0,0 +1,7 @@ +--- +name: grill-with-docs +description: A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go. +disable-model-invocation: false +--- + +Call the Skill tool twice, for "grilling" and "domain-modeling". diff --git a/skills/grill-with-docs/agents/openai.yaml b/skills/grill-with-docs/agents/openai.yaml new file mode 100644 index 0000000..85d0049 --- /dev/null +++ b/skills/grill-with-docs/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Grill With Docs" + short_description: "Explicitly invoke the Grill With Docs workflow" + default_prompt: "Use $grill-with-docs to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/grilling/SKILL.md b/skills/grilling/SKILL.md new file mode 100644 index 0000000..8ca78c6 --- /dev/null +++ b/skills/grilling/SKILL.md @@ -0,0 +1,28 @@ +--- +name: grilling +description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases. +--- + +Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it. + +Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled: the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round. + +Format a round like so: + +``` +❓ **Q1** - ****: + +➡️ + +--- + +❓ **Q2** - ****: + +➡️ +``` + +Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one. + +Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait. + +The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding. diff --git a/skills/grilling/agents/openai.yaml b/skills/grilling/agents/openai.yaml new file mode 100644 index 0000000..ddbdb96 --- /dev/null +++ b/skills/grilling/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Grilling" + short_description: "Stress-test thinking a round of questions at a time" diff --git a/skills/handoff/SKILL.md b/skills/handoff/SKILL.md new file mode 100644 index 0000000..71e9bf9 --- /dev/null +++ b/skills/handoff/SKILL.md @@ -0,0 +1,16 @@ +--- +name: handoff +description: Compact the current conversation into a handoff document for another agent to pick up. +argument-hint: "What will the next session be used for?" +disable-model-invocation: false +--- + +Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace. + +Include a "suggested skills" section in the document, naming which skills the next agent should call the Skill tool for. + +Do not duplicate content already captured in other artifacts (specs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead. + +Redact any sensitive information, such as API keys, passwords, or personally identifiable information. + +If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly. diff --git a/skills/handoff/agents/openai.yaml b/skills/handoff/agents/openai.yaml new file mode 100644 index 0000000..c93d8f4 --- /dev/null +++ b/skills/handoff/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Handoff" + short_description: "Explicitly invoke the Handoff workflow" + default_prompt: "Use $handoff to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/implement-spec/SKILL.md b/skills/implement-spec/SKILL.md new file mode 100644 index 0000000..a24c336 --- /dev/null +++ b/skills/implement-spec/SKILL.md @@ -0,0 +1,35 @@ +--- +name: implement-spec +description: "Implement a specification in code." +disable-model-invocation: false +--- + +You have been provided a spec. This spec should have tickets associated with it, describing how to implement the spec. + +The goal is a PR which implements the entire spec on a single branch. + +The tickets are not a list of steps. They are a **task graph** with blocking relationships between them. This means there is always a **frontier** of tickets which are ready to be grabbed. + +Communication to and from subagents should be sparse. Communicate primarily through **context pointers**: to the spec, tickets, research notes, and previous commits. Don't duplicate information already available via pointers. + +**Implementer subagents** should be run in the background where possible for **maximum concurrency**. + +## Steps + +1. Read the spec and tickets. Read enough to understand the task graph. + +2. (optional) Use an **exploration subagent** to conduct any exploration required by the tickets - relevant codebase files or external documentation. Ensure the exploration subagent can save files - it should save its markdown notes in a directory outside the repo, accessible by all future subagents. This lets **implementer subagents** focus on implementation rather than exploration. + +3. Create a branch, and a draft PR. The PR should be marked as 'closing' the spec issue and tickets. + +4. Use **implementer subagents** to implement each ticket. Each implementer subagent should work in its own worktree, on its own branch. + +5. Once an **implementer subagent** completes, merge its work to the PR branch with a **merger subagent**. + +6. If this changes the **frontier** of available tickets, kick off more **implementer subagents** to work on the new tickets. This allows for maximum concurrency. + +7. Once all tickets are complete, run /code-review on the PR branch. Fix all issues raised by the code review in a single **implementer subagent**. + +8. Mark the PR as ready for review. + +9. Clean up all **implementer subagent** worktrees. diff --git a/skills/implement-spec/agents/openai.yaml b/skills/implement-spec/agents/openai.yaml new file mode 100644 index 0000000..297be45 --- /dev/null +++ b/skills/implement-spec/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Implement Spec" + short_description: "Explicitly invoke the Implement Spec workflow" + default_prompt: "Use $implement-spec to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/implement/SKILL.md b/skills/implement/SKILL.md new file mode 100644 index 0000000..b17c701 --- /dev/null +++ b/skills/implement/SKILL.md @@ -0,0 +1,15 @@ +--- +name: implement +description: "Implement a piece of work based on a spec or set of tickets." +disable-model-invocation: false +--- + +Implement the work described by the user in the spec or tickets. + +Use /tdd where possible, at pre-agreed seams. + +Run typechecking regularly, single test files regularly, and the full test suite once at the end. + +Once done, use /code-review to review the work. + +Commit your work to the current branch. diff --git a/skills/implement/agents/openai.yaml b/skills/implement/agents/openai.yaml new file mode 100644 index 0000000..f7a4ae3 --- /dev/null +++ b/skills/implement/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Implement" + short_description: "Explicitly invoke the Implement workflow" + default_prompt: "Use $implement to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/improve-codebase-architecture/HTML-REPORT.md b/skills/improve-codebase-architecture/HTML-REPORT.md new file mode 100644 index 0000000..e39e825 --- /dev/null +++ b/skills/improve-codebase-architecture/HTML-REPORT.md @@ -0,0 +1,123 @@ +# HTML Report Format + +The architectural review is rendered as a single self-contained HTML file in the OS temp directory. Tailwind and Mermaid both come from CDNs. Mermaid handles graph-shaped diagrams reliably; hand-built divs and inline SVG handle the more editorial visuals (mass diagrams, cross-sections). Mix the two: don't lean on Mermaid for everything, it'll start to look generic. + +## Scaffold + +```html + + + + + Architecture review for {{repo name}} + + + + + +
+
...
+
...
+
...
+
+ + +``` + +## Header + +Repo name, date, and a compact legend: solid box = module, dashed line = seam, red arrow = leakage, thick dark box = deep module. No introduction paragraph. Straight into the candidates. + +## Candidate card + +The diagrams carry the weight. Prose is sparse, plain, and uses the glossary terms (from the `/codebase-design` skill) without ceremony. + +Each candidate is one `
`: + +- **Title**: short, names the deepening (e.g. "Collapse the Order intake pipeline"). +- **Badge row**: recommendation strength (`Strong` = emerald, `Worth exploring` = amber, `Speculative` = slate), plus a tag for the dependency category (`in-process`, `local-substitutable`, `ports & adapters`, `mock`). +- **Files**: monospaced list, `font-mono text-sm`. +- **Before / After diagram**: the centrepiece. Two columns, side by side. See patterns below. +- **Problem**: one sentence. What hurts. +- **Solution**: one sentence. What changes. +- **Wins**: bullets, ≤6 words each. e.g. "Tests hit one interface", "Pricing logic stops leaking", "Delete 4 shallow wrappers". +- **ADR callout** (if applicable): one line in an amber-tinted box. + +No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram. + +## Diagram patterns + +Pick the pattern that fits the candidate. Mix them. Don't make every diagram look the same. Variety is part of the point. + +### Mermaid graph (the workhorse for dependencies / call flow) + +Use a Mermaid `flowchart` or `graph` when the point is "X calls Y calls Z, and look at the mess." Wrap it in a Tailwind-styled card so it doesn't feel parachuted in. Style with classDef to colour leakage edges red and the deep module dark. Sequence diagrams work well for "before: 6 round-trips; after: 1." + +```html +
+
+    flowchart LR
+      A[OrderHandler] --> B[OrderValidator]
+      B --> C[OrderRepo]
+      C -.leak.-> D[PricingClient]
+      classDef leak stroke:#dc2626,stroke-width:2px;
+      class C,D leak
+  
+
+``` + +### Hand-built boxes-and-arrows (when Mermaid's layout fights you) + +Modules as `
`s with borders and labels. Arrows as inline SVG `` or `` elements positioned absolutely over a relative container. Reach for this when you want the "after" diagram to feel like one thick-bordered deep module with greyed-out internals, since Mermaid won't render that with the right weight. + +### Cross-section (good for layered shallowness) + +Stack horizontal bands (`h-12 border-l-4`) to show layers a call passes through. Before: 6 thin layers each doing nothing. After: 1 thick band labelled with the consolidated responsibility. + +### Mass diagram (good for "interface as wide as implementation") + +Two rectangles per module: one for interface surface area, one for implementation. Before: interface rectangle is nearly as tall as the implementation rectangle (shallow). After: interface rectangle is short, implementation rectangle is tall (deep). + +### Call-graph collapse + +Before: a tree of function calls rendered as nested boxes. After: the same tree collapsed into one box, with the now-internal calls shown faded inside it. + +## Style guidance + +- Lean editorial, not corporate-dashboard. Generous whitespace. Serif optional for headings (`font-serif` works well with stone/slate). +- Colour sparingly: one accent (emerald or indigo) plus red for leakage and amber for warnings. +- Keep diagrams ~320px tall so before/after sits comfortably side by side without scrolling. +- Use `text-xs uppercase tracking-wider` for module labels inside diagrams, so they read as schematic, not as UI. +- The only scripts are the Tailwind CDN and the Mermaid ESM import. The report is otherwise static: no app code, no interactivity beyond Mermaid's own rendering. + +## Top recommendation section + +One larger card. Candidate name, one sentence on why, anchor link to its card. That's it. + +## Tone + +Plain English, concise, but the architectural nouns and verbs come straight from the `/codebase-design` skill. Concision is not an excuse to drift. + +**Use exactly:** module, interface, implementation, depth, deep, shallow, seam, adapter, leverage, locality. + +**Never substitute:** component, service, unit (for module) · API, signature (for interface) · boundary (for seam) · layer, wrapper (for module, when you mean module). + +**Phrasings that fit the style:** + +- "Order intake module is shallow: interface nearly matches the implementation." +- "Pricing leaks across the seam." +- "Deepen: one interface, one place to test." +- "Two adapters justify the seam: HTTP in prod, in-memory in tests." + +**Wins bullets** name the gain in glossary terms: *"locality: bugs concentrate in one module"*, *"leverage: one interface, N call sites"*, *"interface shrinks; implementation absorbs the wrappers"*. Don't write *"easier to maintain"* or *"cleaner code"*, because those terms aren't in the glossary and don't earn their place. + +No hedging, no throat-clearing, no "it's worth noting that…". If a sentence could be a bullet, make it a bullet. If a bullet could be cut, cut it. If a term isn't in the `/codebase-design` glossary, reach for one that is before inventing a new one. diff --git a/skills/improve-codebase-architecture/SKILL.md b/skills/improve-codebase-architecture/SKILL.md new file mode 100644 index 0000000..a590073 --- /dev/null +++ b/skills/improve-codebase-architecture/SKILL.md @@ -0,0 +1,71 @@ +--- +name: improve-codebase-architecture +description: Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick. +disable-model-invocation: false +--- + +# Improve Codebase Architecture + +Surface architectural friction and propose **deepening opportunities**: refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability. + +This command is _informed_ by the project's domain model and built on a shared design vocabulary: + +- Call the Skill tool with "codebase-design" for the architecture vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality**) and its principles (the deletion test, "the interface is the test surface", "one adapter = hypothetical seam, two = real"). Use these terms exactly in every suggestion, and don't drift into "component," "service," "API," or "boundary." +- The domain language in `CONTEXT.md` gives names to good seams; ADRs in `docs/adr/` record decisions this command should not re-litigate. + +## Process + +### 1. Explore + +**Scope before you scan: YAGNI.** Deepening a module pays off by making future changes to it easier, so put extra weight on the parts of the codebase that have recently changed. Decide *where* to look before you look: + +- If the user named a direction (a module, a subsystem, a pain point), take it, and skip the inference below. +- Otherwise, walk back a good stretch of the commit history (`git log --oneline`) to find the codebase's hot spots, the files and areas that keep coming up, and let those paths pull your attention first. If the changes are scattered with no clear hot spot, widen the net. + +Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first. + +Then spawn a sub-agent to walk the codebase. Don't follow rigid heuristics; explore organically and note where you experience friction: + +- Where does understanding one concept require bouncing between many small modules? +- Where are modules **shallow**, with an interface nearly as complex as the implementation? +- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)? +- Where do tightly-coupled modules leak across their seams? +- Which parts of the codebase are untested, or hard to test through their current interface? + +Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want. + +### 2. Present candidates as an HTML report + +Write a self-contained HTML file to the OS temp directory so nothing lands in the repo. Resolve the temp dir from `$TMPDIR`, falling back to `/tmp` (or `%TEMP%` on Windows), and write to `/architecture-review-.html` so each run gets a fresh file. Open it for the user (`xdg-open ` on Linux, `open ` on macOS, `start ` on Windows) and tell them the absolute path. + +The report uses **Tailwind via CDN** for layout and styling, and **Mermaid via CDN** for diagrams where a graph/flow/sequence reliably communicates the structure. Mix Mermaid with hand-crafted CSS/SVG visuals: use Mermaid when relationships are graph-shaped (call graphs, dependencies, sequences), and hand-built divs/SVG when you want something more editorial (mass diagrams, cross-sections, collapse animations). Each candidate gets a **before/after visualisation**. Be visual. + +For each candidate, render a card with: + +- **Files**: which files/modules are involved +- **Problem**: why the current architecture is causing friction +- **Solution**: plain English description of what would change +- **Benefits**: explained in terms of locality and leverage, and how tests would improve +- **Before / After diagram**: side-by-side, custom-drawn, illustrating the shallowness and the deepening +- **Recommendation strength**: one of `Strong`, `Worth exploring`, `Speculative`, rendered as a badge + +End the report with a **Top recommendation** section: which candidate you'd tackle first and why. + +**Use CONTEXT.md vocabulary for the domain, and the `/codebase-design` vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module," not "the FooBarHandler," and not "the Order service." + +**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: _"contradicts ADR-0007, but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids. + +See [HTML-REPORT.md](HTML-REPORT.md) for the full HTML scaffold, diagram patterns, and styling guidance. + +Do NOT propose interfaces yet. After the file is written, ask the user: "Which of these would you like to explore?" + +### 3. Grilling loop + +Once the user picks a candidate, call the Skill tool with "grilling" to walk the decision tree with them: constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. + +Side effects happen inline as decisions crystallize; call the Skill tool with "domain-modeling" to keep the domain model current as you go: + +- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md`. Create the file lazily if it doesn't exist. +- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. +- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing; skip ephemeral reasons ("not worth it right now") and self-evident ones. +- **Want to explore alternative interfaces for the deepened module?** Call the Skill tool with "codebase-design" and use its design-it-twice parallel sub-agent pattern. diff --git a/skills/improve-codebase-architecture/agents/openai.yaml b/skills/improve-codebase-architecture/agents/openai.yaml new file mode 100644 index 0000000..5ff703c --- /dev/null +++ b/skills/improve-codebase-architecture/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Improve Codebase Architecture" + short_description: "Explicitly invoke the Improve Codebase Architecture workflow" + default_prompt: "Use $improve-codebase-architecture to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/loop-me/SKILL.md b/skills/loop-me/SKILL.md new file mode 100644 index 0000000..4766828 --- /dev/null +++ b/skills/loop-me/SKILL.md @@ -0,0 +1,32 @@ +--- +name: loop-me +description: Grill me about specs for the workflows I want to build, within this workspace. +disable-model-invocation: false +argument-hint: "A workflow to design, or nothing to go find one" +--- + +Run a stateful `/grilling` session whose only output is **workflow** specs. Use the grilling discipline (relentless, a round of questions at a time, a recommended answer attached to each) aimed at the vocabulary and goal below. Create, edit, and delete specs as the grilling resolves things. + +## The loop lens + +A **loop** is a recurring pattern in the user's life: their career, their week, their morning, a single repeated activity. Picturing a life as loops within loops reveals how predictable its activities really are, which is what makes them worth **delegating**. Use the lens to find loops worth specifying, and propose ones the user hasn't noticed. + +A **workflow** is the spec of one loop, made real. You run a workflow on a loop: the loop is its running instantiation. Workflows live in `workflows/*.md` and are the source of truth. + +## Vocabulary + +A shared language, reached for only when a workflow calls for it: never a checklist. **Mandate nothing structural**: a workflow needs no AI, no checkpoint, and no schedule unless the grilling shows it does. + +- **Trigger**: what fires each run, an **event** (a new email, a new issue) or a **schedule** (every morning). Event-triggering is usually the more efficient. +- **Checkpoint**: a human-in-the-loop point where the user is asked to verify or decide. Some workflows have none and run autonomously; some use no AI at all. +- **Push right**: defer the checkpoint as far as it will go. Do maximal work before involving the human, so they are asked once, late, with everything prepared. +- **Brief**: what a checkpoint presents, a tight, decision-ready summary (what was produced, why, and a link down to the asset itself), never the raw output. The user reads a brief, not a draft. Speed of review is imperative. + +## Definition of done + +A workflow spec is done when an implementer agent could build it without asking a single question. Grill until then; nothing is done while a question remains. + +## The workspace + +- `workflows/*.md`: one spec per workflow. +- `NOTES.md`: raw notes on the user's world, the tools they use, the channels they process, and their own terminology for both. When it is empty or thin, interview them about their world before specifying anything. Sharpen fuzzy terms into canonical ones as they surface, and record them here. diff --git a/skills/loop-me/agents/openai.yaml b/skills/loop-me/agents/openai.yaml new file mode 100644 index 0000000..5a99454 --- /dev/null +++ b/skills/loop-me/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Loop Me" + short_description: "Explicitly invoke the Loop Me workflow" + default_prompt: "Use $loop-me to guide this task." +policy: + allow_implicit_invocation: false diff --git a/skills/migrate-to-shoehorn/SKILL.md b/skills/migrate-to-shoehorn/SKILL.md new file mode 100644 index 0000000..ae4f965 --- /dev/null +++ b/skills/migrate-to-shoehorn/SKILL.md @@ -0,0 +1,118 @@ +--- +name: migrate-to-shoehorn +description: Migrate test files from `as` type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace `as` in tests, or needs partial test data. +--- + +# Migrate to Shoehorn + +## Why shoehorn? + +`shoehorn` lets you pass partial data in tests while keeping TypeScript happy. It replaces `as` assertions with type-safe alternatives. + +**Test code only.** Never use shoehorn in production code. + +Problems with `as` in tests: + +- Trained not to use it +- Must manually specify target type +- Double-as (`as unknown as Type`) for intentionally wrong data + +## Install + +```bash +npm i @total-typescript/shoehorn +``` + +## Migration patterns + +### Large objects with few needed properties + +Before: + +```ts +type Request = { + body: { id: string }; + headers: Record; + cookies: Record; + // ...20 more properties +}; + +it("gets user by id", () => { + // Only care about body.id but must fake entire Request + getUser({ + body: { id: "123" }, + headers: {}, + cookies: {}, + // ...fake all 20 properties + }); +}); +``` + +After: + +```ts +import { fromPartial } from "@total-typescript/shoehorn"; + +it("gets user by id", () => { + getUser( + fromPartial({ + body: { id: "123" }, + }), + ); +}); +``` + +### `as Type` → `fromPartial()` + +Before: + +```ts +getUser({ body: { id: "123" } } as Request); +``` + +After: + +```ts +import { fromPartial } from "@total-typescript/shoehorn"; + +getUser(fromPartial({ body: { id: "123" } })); +``` + +### `as unknown as Type` → `fromAny()` + +Before: + +```ts +getUser({ body: { id: 123 } } as unknown as Request); // wrong type on purpose +``` + +After: + +```ts +import { fromAny } from "@total-typescript/shoehorn"; + +getUser(fromAny({ body: { id: 123 } })); +``` + +## When to use each + +| Function | Use case | +| --------------- | -------------------------------------------------- | +| `fromPartial()` | Pass partial data that still type-checks | +| `fromAny()` | Pass intentionally wrong data (keeps autocomplete) | +| `fromExact()` | Force full object (swap with fromPartial later) | + +## Workflow + +1. **Gather requirements** - ask user: + - What test files have `as` assertions causing problems? + - Are they dealing with large objects where only some properties matter? + - Do they need to pass intentionally wrong data for error testing? + +2. **Install and migrate**: + - [ ] Install: `npm i @total-typescript/shoehorn` + - [ ] Find test files with `as` assertions: `grep -r " as [A-Z]" --include="*.test.ts" --include="*.spec.ts"` + - [ ] Replace `as Type` with `fromPartial()` + - [ ] Replace `as unknown as Type` with `fromAny()` + - [ ] Add imports from `@total-typescript/shoehorn` + - [ ] Run type check to verify diff --git a/skills/migrate-to-shoehorn/agents/openai.yaml b/skills/migrate-to-shoehorn/agents/openai.yaml new file mode 100644 index 0000000..3bd79ee --- /dev/null +++ b/skills/migrate-to-shoehorn/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Migrate to Shoehorn" + short_description: "Replace test assertions with shoehorn" diff --git a/skills/prototype/LOGIC.md b/skills/prototype/LOGIC.md new file mode 100644 index 0000000..32be86a --- /dev/null +++ b/skills/prototype/LOGIC.md @@ -0,0 +1,67 @@ +# Logic Prototype + +A single, self-contained HTML file (a **shareable demo**) that lets anyone drive a state model by clicking buttons. Use this when the question is about **business logic, state transitions, or data shape**: the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases. + +Because it's one file with nothing to install, you can hand it to a non-developer (a designer, a PM, a domain expert) and let them feel the model for themselves. So it speaks their language, not the code's. + +## When this is the right shape + +- "I'm not sure if this state machine handles the edge case where X then Y." +- "Does this data model actually let me represent the case where..." +- "I want to feel out what the API should look like before writing it." +- Anything where someone wants to **press buttons and watch state change**. + +If the question is "what should this look like," this is the wrong branch. Use [UI.md](UI.md). + +## Process + +### 1. State the question + +Before writing code, write down what state model and what question you're prototyping. One paragraph, at the top of the demo (in a visible intro, not just a comment). A logic prototype that answers the wrong question is pure waste, so make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK. + +### 2. Isolate the logic in a portable module + +Put the actual logic (the bit that's answering the question) in a single `', + ); +}); +server.listen(Number(process.env.SPECIAL_OFFERING_PREVIEW_PORT ?? 3002), "127.0.0.1", () => { + console.log(`Test-only controlled-response preview: http://127.0.0.1:${server.address().port}`); +}); diff --git a/src/components/OfferingsPage.tsx b/src/components/OfferingsPage.tsx index f4c2748..e4101d0 100644 --- a/src/components/OfferingsPage.tsx +++ b/src/components/OfferingsPage.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from "react"; -import type { Offering } from "@/types/offering"; +import type { Offering, OfferingErrorResponse } from "@/types/offering"; import styles from "@/styles/offerings.module.css"; interface OfferingsPageProps { @@ -11,33 +11,65 @@ interface OfferingsPageProps { specialsOnly?: boolean; } +async function readErrorMessage(response: Response, fallback: string): Promise { + try { + const body = (await response.json()) as Partial; + const message = body?.error?.message; + return typeof message === "string" && message.trim() !== "" ? message : fallback; + } catch { + return fallback; + } +} + export default function OfferingsPage({ title, intro, emptyMessage, specialsOnly = false }: OfferingsPageProps) { const [offerings, setOfferings] = useState([]); - const [isLoading, setIsLoading] = useState(true); + const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { + let isCurrent = true; + const fallbackMessage = `Unable to load ${title.toLowerCase()}.`; + async function loadOfferings() { + setIsLoading(true); + setError(null); + try { const response = await fetch("/api/offerings"); if (!response.ok) { - throw new Error(`Unable to load ${title.toLowerCase()}.`); + throw new Error(await readErrorMessage(response, fallbackMessage)); } - const data: Offering[] = await response.json(); - setOfferings(data); + const data: unknown = await response.json(); + + if (!Array.isArray(data)) { + throw new Error(fallbackMessage); + } + + if (isCurrent) { + setOfferings(data as Offering[]); + } } catch (requestError) { - setError(requestError instanceof Error ? requestError.message : `Unable to load ${title.toLowerCase()}.`); + if (isCurrent) { + setOfferings([]); + setError(requestError instanceof Error ? requestError.message : fallbackMessage); + } } finally { - setIsLoading(false); + if (isCurrent) { + setIsLoading(false); + } } } loadOfferings(); + + return () => { + isCurrent = false; + }; }, [title]); - const visibleOfferings = specialsOnly ? offerings.filter((offering) => offering.specialOffer) : offerings; + const visibleOfferings = specialsOnly ? offerings.filter((offering) => offering.specialOffer === true) : offerings; return (
@@ -47,7 +79,7 @@ export default function OfferingsPage({ title, intro, emptyMessage, specialsOnly

{intro}

- {isLoading &&

Loading {title.toLowerCase()}...

} + {isLoading &&

Loading {title.toLowerCase()}...

} {error &&

{error}

} {!isLoading && !error && visibleOfferings.length === 0 &&

{emptyMessage}

} {!isLoading && !error && visibleOfferings.length > 0 && ( diff --git a/src/components/SpecialOfferingControl.tsx b/src/components/SpecialOfferingControl.tsx new file mode 100644 index 0000000..d181539 --- /dev/null +++ b/src/components/SpecialOfferingControl.tsx @@ -0,0 +1,19 @@ +"use client"; + +import { useId } from "react"; +import styles from "@/styles/special-offering-control.module.css"; + +type SpecialOfferingControlProps = { + checked: boolean; + onChange: (next: boolean) => void; +}; + +export default function SpecialOfferingControl({ checked, onChange }: SpecialOfferingControlProps) { + const id = useId(); + return ( + + ); +} diff --git a/src/styles/special-offering-control.module.css b/src/styles/special-offering-control.module.css new file mode 100644 index 0000000..b6f967f --- /dev/null +++ b/src/styles/special-offering-control.module.css @@ -0,0 +1,20 @@ +.control { + display: inline-flex; + align-items: center; + gap: 0.75rem; + min-height: 44px; + padding: 0.25rem; + cursor: pointer; +} + +.control input { + width: 1.25rem; + height: 1.25rem; + margin: 0; + accent-color: #8a4b2a; +} + +.control input:focus-visible { + outline: 3px solid #8a4b2a; + outline-offset: 4px; +} diff --git a/tests/offerings-http.test.mjs b/tests/offerings-http.test.mjs index 994fd77..3b45cc3 100644 --- a/tests/offerings-http.test.mjs +++ b/tests/offerings-http.test.mjs @@ -1,5 +1,7 @@ import assert from "node:assert/strict"; -import { after, before, test } from "node:test"; +import { createRequire } from "node:module"; +import { after, afterEach, before, describe, it, mock, test } from "node:test"; +import { JSDOM } from "jsdom"; import { startHttpServer } from "./helpers/http-server.mjs"; let server; @@ -77,3 +79,196 @@ test("GET flags exactly the three starting special offerings", async () => { ["Pumpkin Spice Latte", "Golden Hour Cold Brew", "Hojicha Latte"], ); }); + +// Test-only fixtures. Names, categories, and flags mirror the starting menu. +const offeringFixture = (ID, name, category, specialOffer, price = 4.5) => ({ + ID, + name, + description: `${name} description`, + price, + category, + specialOffer, +}); +const ordinaryItems = [ + offeringFixture("test-cappuccino", "Cappuccino", "Coffee", false, 5.9), + offeringFixture("test-mocha", "Mocha", "Coffee", false), + offeringFixture("test-matcha", "Matcha", "Tea", false), + offeringFixture("test-smoothie", "Fruit Smoothie", "Smoothie", false), +]; +const startingSpecials = [ + offeringFixture("test-psl", "Pumpkin Spice Latte", "Coffee", true), + offeringFixture("test-golden-hour", "Golden Hour Cold Brew", "Coffee", true), + offeringFixture("test-hojicha", "Hojicha Latte", "Tea", true), +]; +const newSpecial = offeringFixture("test-cardamom", "Cardamom Latte", "Coffee", true); + +const menuProps = { title: "Menu", intro: "intro", emptyMessage: "Nothing here yet." }; +const specialsProps = { title: "Specials", intro: "intro", emptyMessage: "No specials right now.", specialsOnly: true }; + +const jsonResponse = (body, status = 200) => + new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } }); + +describe("OfferingsPage with controlled responses", () => { + const realFetch = globalThis.fetch; + const mounted = []; + let React; + let createRoot; + let act; + let fetchCalls = []; + let OfferingsPage; + + before(async () => { + const require = createRequire(import.meta.url); + require.extensions[".css"] = (module) => { + module.exports = {}; + }; + + const dom = new JSDOM("", { url: "http://localhost/" }); + const define = (key, value) => + Object.defineProperty(globalThis, key, { value, configurable: true, writable: true }); + define("window", dom.window); + define("document", dom.window.document); + define("navigator", dom.window.navigator); + define("IS_REACT_ACT_ENVIRONMENT", true); + + React = ((m) => m.default ?? m)(await import("react")); + createRoot = ((m) => m.default ?? m)(await import("react-dom/client")).createRoot; + act = React.act ?? React.unstable_act; + globalThis.React = React; + OfferingsPage = require("../src/components/OfferingsPage.tsx").default; + }); + + afterEach(async () => { + while (mounted.length) { + const { root, container } = mounted.pop(); + try { + await act(async () => root.unmount()); + } catch { + // already unmounted + } + container.remove(); + } + globalThis.fetch = realFetch; + mock.restoreAll(); + }); + + function stubFetch(handler) { + fetchCalls = []; + globalThis.fetch = async (url) => { + fetchCalls.push(String(url)); + return handler(); + }; + } + + async function renderPage(props) { + const container = document.createElement("div"); + document.body.appendChild(container); + const root = createRoot(container); + mounted.push({ root, container }); + await act(async () => root.render(React.createElement(OfferingsPage, props))); + return container; + } + + async function waitFor(check, tries = 50) { + for (let i = 0; i < tries; i += 1) { + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + try { + return check(); + } catch { + // keep waiting + } + } + return check(); + } + + const itemNames = (container) => [...container.querySelectorAll("li h2")].map((node) => node.textContent).sort(); + const namesOf = (list) => list.map((offering) => offering.name).sort(); + const alertText = (container) => container.querySelector('[role="alert"]')?.textContent; + + it("shows loading while the request is pending", async () => { + stubFetch(() => new Promise(() => {})); + const container = await renderPage(menuProps); + assert.match(container.textContent, /Loading menu\.\.\./); + }); + + for (const [label, props] of [ + ["Menu", menuProps], + ["Specials", specialsProps], + ]) { + it(`shows the empty message for ${label} on []`, async () => { + stubFetch(() => jsonResponse([])); + const container = await renderPage(props); + await waitFor(() => assert.match(container.textContent, new RegExp(props.emptyMessage))); + assert.equal(container.querySelectorAll("li").length, 0); + }); + } + + it("shows the envelope message and no items or empty message on a 503", async () => { + stubFetch(() => jsonResponse({ error: { code: "DATABASE_UNAVAILABLE", message: "Please try again." } }, 503)); + const container = await renderPage(menuProps); + await waitFor(() => assert.equal(alertText(container), "Please try again.")); + assert.equal(container.querySelectorAll("li").length, 0); + assert.doesNotMatch(container.textContent, new RegExp(menuProps.emptyMessage)); + }); + + it("falls back to the generic message when a failure body is not the envelope", async () => { + stubFetch(() => new Response("bad gateway", { status: 502 })); + const container = await renderPage(menuProps); + await waitFor(() => assert.equal(alertText(container), "Unable to load menu.")); + }); + + it("shows an alert when the request rejects", async () => { + stubFetch(() => Promise.reject(new Error("offline"))); + const container = await renderPage(menuProps); + await waitFor(() => assert.equal(alertText(container), "offline")); + }); + + it("treats a non-array 200 body as a failure", async () => { + stubFetch(() => jsonResponse({ not: "an array" })); + const container = await renderPage(menuProps); + await waitFor(() => assert.equal(alertText(container), "Unable to load menu.")); + assert.equal(container.querySelectorAll("li").length, 0); + }); + + it("Menu includes every response item from /api/offerings", async () => { + const all = [...ordinaryItems, ...startingSpecials]; + stubFetch(() => jsonResponse(all)); + const container = await renderPage(menuProps); + await waitFor(() => assert.equal(container.querySelectorAll("li").length, all.length)); + assert.deepEqual(itemNames(container), namesOf(all)); + assert.deepEqual(fetchCalls, ["/api/offerings"]); + assert.match(container.textContent, /\$5\.90/); + }); + + it("Specials shows exactly the three starting specials and no ordinary items", async () => { + stubFetch(() => jsonResponse([...ordinaryItems, ...startingSpecials])); + const container = await renderPage(specialsProps); + await waitFor(() => assert.equal(container.querySelectorAll("li").length, 3)); + assert.deepEqual(itemNames(container), namesOf(startingSpecials)); + for (const item of ordinaryItems) assert.doesNotMatch(container.textContent, new RegExp(item.name)); + }); + + it("shows a newly supplied special in Specials and Menu (fresh mount, not live update)", async () => { + const withNew = [...ordinaryItems, ...startingSpecials, newSpecial]; + stubFetch(() => jsonResponse(withNew)); + const specials = await renderPage(specialsProps); + await waitFor(() => assert.equal(specials.querySelectorAll("li").length, 4)); + assert.ok(itemNames(specials).includes("Cardamom Latte")); + const menu = await renderPage(menuProps); + await waitFor(() => assert.equal(menu.querySelectorAll("li").length, withNew.length)); + assert.ok(itemNames(menu).includes("Cardamom Latte")); + }); + + it("uses string IDs as keys without React key warnings", async () => { + const all = [...ordinaryItems, ...startingSpecials, newSpecial]; + assert.ok(all.every((offering) => typeof offering.ID === "string" && offering.ID !== "")); + assert.equal(new Set(all.map((offering) => offering.ID)).size, all.length); + const errors = mock.method(console, "error", () => {}); + stubFetch(() => jsonResponse(all)); + const container = await renderPage(menuProps); + await waitFor(() => assert.equal(container.querySelectorAll("li").length, all.length)); + assert.equal(errors.mock.calls.filter((call) => /key/i.test(String(call.arguments[0]))).length, 0); + }); +}); diff --git a/tests/previews/special-offerings.tsx b/tests/previews/special-offerings.tsx new file mode 100644 index 0000000..ee158ca --- /dev/null +++ b/tests/previews/special-offerings.tsx @@ -0,0 +1,39 @@ +import React, { useState } from "react"; +import { createRoot } from "react-dom/client"; +import OfferingsPage from "@/components/OfferingsPage"; +import SpecialOfferingControl from "@/components/SpecialOfferingControl"; + +const scenario = new URLSearchParams(window.location.search).get("scenario") ?? "starting"; +const originalFetch = window.fetch.bind(window); +window.fetch = (input, init) => + originalFetch(input === "/api/offerings" ? `/api/offerings?scenario=${scenario}` : input, init); + +function Preview() { + const [checked, setChecked] = useState(false); + return ( + <> +
+

Controlled-response verification

+

Test-only preview. No database or persistence. Scenario: {scenario}.

+ + +

specialOffer: {String(checked)}

+
+ + + + ); +} + +createRoot(document.getElementById("root")!).render(); diff --git a/tests/special-offering-control.test.mjs b/tests/special-offering-control.test.mjs new file mode 100644 index 0000000..ac23053 --- /dev/null +++ b/tests/special-offering-control.test.mjs @@ -0,0 +1,52 @@ +import assert from "node:assert/strict"; +import { createRequire } from "node:module"; +import test from "node:test"; +import React, { act } from "react"; +import { JSDOM } from "jsdom"; + +const dom = new JSDOM(""); +globalThis.window = dom.window; +globalThis.document = dom.window.document; +Object.defineProperty(globalThis, "navigator", { value: dom.window.navigator, configurable: true }); +globalThis.IS_REACT_ACT_ENVIRONMENT = true; +globalThis.React = React; +const require = createRequire(import.meta.url); +require.extensions[".css"] = (module) => { + module.exports = {}; +}; +const { createRoot } = await import("react-dom/client"); + +test("the labeled special control follows its parent and emits true and false", async () => { + const SpecialOfferingControl = require("../src/components/SpecialOfferingControl.tsx").default; + const changes = []; + function Parent() { + const [checked, setChecked] = React.useState(false); + return React.createElement(SpecialOfferingControl, { + checked, + onChange(next) { + changes.push(next); + setChecked(next); + }, + }); + } + const container = document.createElement("div"); + document.body.append(container); + const root = createRoot(container); + try { + await act(async () => root.render(React.createElement(Parent))); + const checkbox = container.querySelector('input[type="checkbox"]'); + assert.ok(checkbox.labels[0].textContent.includes("Special offering")); + assert.equal(checkbox.checked, false); + await act(async () => checkbox.click()); + assert.equal(checkbox.checked, true); + await act(async () => checkbox.click()); + assert.equal(checkbox.checked, false); + assert.deepEqual(changes, [true, false]); + await act(async () => root.render(React.createElement(SpecialOfferingControl, { checked: true, onChange() {} }))); + assert.equal(container.querySelector("input").checked, true); + } finally { + await act(async () => root.unmount()); + container.remove(); + dom.window.close(); + } +}); From bcf01c0f1a8f60155f2c8f920d6a6eede92b4984 Mon Sep 17 00:00:00 2001 From: Naomi Mutebi <140992765+Nomm2317@users.noreply.github.com> Date: Wed, 7 Oct 2026 19:26:03 -0700 Subject: [PATCH 24/29] feat(offerings): add tested storage and repeatable seeding (#29) * created offering of drinks * fix: prepare offering data for review * feat(offerings): implement storage and repeatable seeding --------- Co-authored-by: Brian Li --- docs/offering-storage.md | 74 ++++++++ package.json | 4 +- scripts/seed-offerings.ts | 45 +++++ src/database/db.ts | 4 +- src/database/offeringModel.ts | 33 ++++ src/database/offeringStore.ts | 28 +++ src/database/seedOffering.ts | 21 +++ tests/helpers/offering-storage-connection.ts | 27 +++ tests/offering-seed-command.test.mjs | 107 +++++++++++ tests/offering-storage.test.ts | 177 +++++++++++++++++++ 10 files changed, 518 insertions(+), 2 deletions(-) create mode 100644 docs/offering-storage.md create mode 100644 scripts/seed-offerings.ts create mode 100644 src/database/offeringModel.ts create mode 100644 src/database/offeringStore.ts create mode 100644 src/database/seedOffering.ts create mode 100644 tests/helpers/offering-storage-connection.ts create mode 100644 tests/offering-seed-command.test.mjs create mode 100644 tests/offering-storage.test.ts diff --git a/docs/offering-storage.md b/docs/offering-storage.md new file mode 100644 index 0000000..6d4e1d5 --- /dev/null +++ b/docs/offering-storage.md @@ -0,0 +1,74 @@ +# Offering storage and repeatable seeding + +[Issue #15](https://github.com/BrianLiDesign/coffee-shop/issues/15) supplies real MongoDB storage independently of the live API and customer pages. The existing fixture-backed route remains unchanged. Brian composes the real storage with the independently developed handlers under parent [#12](https://github.com/BrianLiDesign/coffee-shop/issues/12). + +## Storage boundary + +Import `offeringStore` from `@/database/offeringStore`: + +- `list(): Promise` returns only the six public fields, sorted by name then string ID using the shared sort rule. An empty collection returns `[]`. +- `create(input: CreateOfferingInput): Promise` resolves after MongoDB acknowledges persistence. MongoDB generates identity, serialized through public `ID`. Internal `_id`, `__v`, and `seedKey` never appear in public results. +- Both operations use the existing `connectDB` helper. Connection selection is bounded to five seconds. Failures reject; handlers own the sanitized 503 error envelope. There is no fixture fallback or automatic seeding. + +`src/database/offeringModel.ts` is separate from the starter User model. It enforces trimmed required name/description, their 100/500-character limits, Coffee/Tea/Smoothie categories, finite nonnegative price with at most two decimal places, and required boolean `specialOffer` defaulting to false. Mongoose's casting is not raw JSON validation; #16 must reject invalid raw request types before calling storage. + +An illustrative public result (MongoDB-generated IDs vary): + +```json +{ + "ID": "507f1f77bcf86cd799439011", + "name": "Vanilla Latte", + "description": "Espresso with vanilla and milk", + "price": 5.75, + "category": "Coffee", + "specialOffer": false +} +``` + +## Independent verification + +```bash +npm ci +npm run test:storage +npm test +npm run typecheck +npm run lint +npm run build +``` + +The tests launch and stop their own disposable local MongoDB 7.0.24 instances using the already installed `mongodb-memory-server-core`. They override inherited `MONGO_URI` with their own isolated target and do not use Atlas. The first run may download a platform-specific MongoDB binary; a blocked download or unsupported operating system is a setup failure, not a passing persistence check. + +Evidence covers real creation/read-back, public serialization, empty reads, duplicate-name ordering, model constraints/defaults, all 16 starting names and three specials, rerun preservation of IDs/count/edits/unrelated records, and command reruns across separate processes. Raw collection writes are used only to arrange duplicate-ID ordering and operator edits; assertions read the public storage boundary. + +Connection evidence deliberately targets an unavailable loopback port with test-only credentials. List/create/seed reject, the test caller emits the shared safe envelope, and a retry against disposable MongoDB succeeds. Expected safe output includes: + +```text +{"error":{"code":"DATABASE_UNAVAILABLE","message":"Database is unavailable."}} +PASS recovery: failed connections do not poison subsequent persistence. +FAIL seed: database operation failed; check configuration, permissions, and connectivity. Reruns preserve existing records. +``` + +No connection string, password, or raw database exception is printed by the command. + +## Deliberate seed command + +Set `MONGO_URI` in private `.env.local` or the environment with an explicit non-system database name. Environment configuration takes precedence. Use an isolated local development database for practice. Then deliberately apply the starting dataset: + +```bash +npm run db:seed -- --apply +``` + +The command inserts the 16 starting offerings from the existing menu dataset, including Pumpkin Spice Latte, Golden Hour Cold Brew, and Hojicha Latte as specials. The fixture IDs supply stable internal seed keys (`coffee-shop-v1-1` through `coffee-shop-v1-16`); they never become database/public IDs. A unique partial index permits multiple unrelated offerings without seed keys. Insert-only upserts preserve existing seeded records even after they are renamed or edited. No collection clearing, deletion, or update of existing menu details occurs. A failed partial run can be rerun safely. + +```text +PASS seed: inserted=16; existing offerings preserved. +PASS seed: inserted=0; existing offerings preserved. +``` + +Missing `--apply`, placeholders, missing/implicit database names, or system databases fail configuration with exit code 1. Connection/write failures produce a sanitized message and exit code 1. The command has a 20-second overall deadline and closes its connection on ordinary completion/failure. + +## Remaining parent evidence + +Live Atlas verification is **pending under #12**. This independently verified storage deliverable does not prove integrated GET/POST, browser refresh/restart persistence, developer Atlas permissions, or completion of Milestone 2. Those checks remain mandatory in the parent integration assignment. + +Implementation references: [Mongoose 8 validation](https://mongoosejs.com/docs/8.x/docs/validation.html) and [bulk writes](). Context7 lookup failed with `fetch failed`; the official version-matched documentation was checked directly. diff --git a/package.json b/package.json index 8095fc3..e20cc91 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "lint:fix": "next lint --fix", "format": "prettier --write .", "preview:offerings": "node scripts/offering-preview-server.mjs", - "test": "node --test --import tsx tests/offerings-http.test.mjs tests/database-checker.test.mjs tests/offering-form.test.mjs src/lib/offerings.test.ts tests/special-offering-control.test.mjs", + "test": "node --test --import tsx tests/offerings-http.test.mjs tests/database-checker.test.mjs tests/offering-form.test.mjs src/lib/offerings.test.ts tests/special-offering-control.test.mjs tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", + "test:storage": "node --test --import tsx tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", + "db:seed": "node --import tsx scripts/seed-offerings.ts", "test:db": "node --test tests/database-checker.test.mjs", "db:check": "node scripts/check-database.mjs", "db:check:write": "node scripts/check-database.mjs --write", diff --git a/scripts/seed-offerings.ts b/scripts/seed-offerings.ts new file mode 100644 index 0000000..05168de --- /dev/null +++ b/scripts/seed-offerings.ts @@ -0,0 +1,45 @@ +import { loadEnvConfig } from "@next/env"; +import mongoose from "mongoose"; +import { seedOfferings } from "@/database/seedOffering"; + +async function main(): Promise { + let stage = "configuration"; + const deadline = setTimeout(() => { + console.error("FAIL seed: 20-second deadline exceeded; rerun safely after checking connectivity."); + process.exit(1); + }, 20_000); + try { + const args = process.argv.slice(2); + if (args.length !== 1 || args[0] !== "--apply") throw new Error("configuration"); + loadEnvConfig(process.cwd(), true, { info() {}, error() {} }); + const uri = process.env.MONGO_URI; + const match = /^mongodb(?:\+srv)?:\/\/[^/]+\/([A-Za-z0-9_-]+)(?:\?.*)?$/.exec(uri ?? ""); + if ( + !match || + /[{}<>]|placeholder|YOUR_|CHANGE_ME/i.test(uri ?? "") || + ["admin", "local", "config"].includes(match[1]) + ) { + throw new Error("configuration"); + } + stage = "seed"; + const inserted = await seedOfferings(); + console.log(`PASS seed: inserted=${inserted}; existing offerings preserved.`); + } catch { + process.exitCode = 1; + console.error( + stage === "configuration" + ? "FAIL configuration: run npm run db:seed -- --apply with MONGO_URI naming an explicit non-system database." + : "FAIL seed: database operation failed; check configuration, permissions, and connectivity. Reruns preserve existing records.", + ); + } finally { + try { + await mongoose.disconnect(); + } catch { + process.exitCode = 1; + console.error("FAIL close: database connection could not be closed."); + } + clearTimeout(deadline); + } +} + +void main(); diff --git a/src/database/db.ts b/src/database/db.ts index 4b523e1..cb1df52 100644 --- a/src/database/db.ts +++ b/src/database/db.ts @@ -31,7 +31,9 @@ const connectDB = async (): Promise => { } if (!cached.promise) { - cached.promise = mongoose.connect(url).then(() => mongoose); + cached.promise = mongoose + .connect(url, { serverSelectionTimeoutMS: 5000, connectTimeoutMS: 5000, socketTimeoutMS: 10000 }) + .then(() => mongoose); } try { diff --git a/src/database/offeringModel.ts b/src/database/offeringModel.ts new file mode 100644 index 0000000..859dc7d --- /dev/null +++ b/src/database/offeringModel.ts @@ -0,0 +1,33 @@ +import mongoose, { Schema, type Model, type Types } from "mongoose"; +import { DEFAULT_SPECIAL_OFFER, OFFERING_CATEGORIES, OFFERING_INPUT_LIMITS, type Offering } from "@/types/offering"; + +export type OfferingRecord = Omit & { _id: Types.ObjectId; seedKey?: string }; + +const offeringSchema = new Schema({ + name: { type: String, required: true, trim: true, maxlength: OFFERING_INPUT_LIMITS.name }, + description: { type: String, required: true, trim: true, maxlength: OFFERING_INPUT_LIMITS.description }, + price: { + type: Number, + required: true, + min: 0, + validate: { + validator: (value: number) => { + const cents = value * 10 ** OFFERING_INPUT_LIMITS.priceDecimalPlaces; + return Number.isFinite(value) && Math.abs(cents - Math.round(cents)) <= 1e-8; + }, + message: "Price must be finite and have at most two decimal places.", + }, + }, + category: { type: String, required: true, enum: OFFERING_CATEGORIES }, + specialOffer: { type: Boolean, required: true, default: DEFAULT_SPECIAL_OFFER }, + seedKey: { type: String, immutable: true }, +}); + +// Unseeded offerings may share names and have no seedKey. Only seed identities are unique. +offeringSchema.index({ seedKey: 1 }, { unique: true, partialFilterExpression: { seedKey: { $type: "string" } } }); + +const OfferingModel = + (mongoose.models.Offering as Model | undefined) ?? + mongoose.model("Offering", offeringSchema); + +export default OfferingModel; diff --git a/src/database/offeringStore.ts b/src/database/offeringStore.ts new file mode 100644 index 0000000..73a7373 --- /dev/null +++ b/src/database/offeringStore.ts @@ -0,0 +1,28 @@ +import connectDB from "@/database/db"; +import OfferingModel, { type OfferingRecord } from "@/database/offeringModel"; +import { sortOfferings } from "@/lib/offerings"; +import type { CreateOfferingInput, Offering } from "@/types/offering"; + +function publicOffering(record: OfferingRecord): Offering { + return { + ID: record._id.toString(), + name: record.name, + description: record.description, + price: record.price, + category: record.category, + specialOffer: record.specialOffer, + }; +} + +/** Real storage boundary supplied to the separately developed request handlers. */ +export const offeringStore = { + async list(): Promise { + await connectDB(); + return sortOfferings((await OfferingModel.find().lean()).map(publicOffering)); + }, + async create(input: CreateOfferingInput): Promise { + await connectDB(); + const { name, description, price, category, specialOffer } = input; + return publicOffering(await OfferingModel.create({ name, description, price, category, specialOffer })); + }, +}; diff --git a/src/database/seedOffering.ts b/src/database/seedOffering.ts new file mode 100644 index 0000000..c77369f --- /dev/null +++ b/src/database/seedOffering.ts @@ -0,0 +1,21 @@ +import connectDB from "@/database/db"; +import OfferingModel from "@/database/offeringModel"; +import { OfferingList } from "@/database/offering"; + +/** Deliberate starting-data import, never a runtime fallback or automatic startup action. */ +export async function seedOfferings(): Promise { + await connectDB(); + await OfferingModel.init(); + const operations = OfferingList.map(({ ID, ...details }) => { + const seedKey = `coffee-shop-v1-${ID}`; + return { + updateOne: { + filter: { seedKey }, + update: { $setOnInsert: { ...details, seedKey } }, + upsert: true, + }, + }; + }); + const result = await OfferingModel.bulkWrite(operations); + return result.upsertedCount; +} diff --git a/tests/helpers/offering-storage-connection.ts b/tests/helpers/offering-storage-connection.ts new file mode 100644 index 0000000..154b685 --- /dev/null +++ b/tests/helpers/offering-storage-connection.ts @@ -0,0 +1,27 @@ +import assert from "node:assert/strict"; +import mongoose from "mongoose"; +import { offeringStore } from "@/database/offeringStore"; +import { seedOfferings } from "@/database/seedOffering"; +import { toErrorEnvelope } from "@/lib/offerings"; + +async function main(): Promise { + try { + const input = { name: "Recovery", description: "Persisted after retry", price: 1, category: "Tea" as const }; + for (const operation of [() => offeringStore.list(), () => offeringStore.create(input), () => seedOfferings()]) { + await assert.rejects(operation()); + } + // The handler owns sanitization. This test caller proves rejected storage errors can be handled safely. + console.log(JSON.stringify(toErrorEnvelope("DATABASE_UNAVAILABLE", "Database is unavailable."))); + process.env.MONGO_URI = process.env.COFFEE_STORAGE_RECOVERY_URI; + const saved = await offeringStore.create(input); + assert.deepEqual(await offeringStore.list(), [saved]); + console.log("PASS recovery: failed connections do not poison subsequent persistence."); + } finally { + await mongoose.disconnect(); + } +} + +void main().catch(() => { + process.exitCode = 1; + console.error("FAIL storage connection evidence."); +}); diff --git a/tests/offering-seed-command.test.mjs b/tests/offering-seed-command.test.mjs new file mode 100644 index 0000000..23cafe9 --- /dev/null +++ b/tests/offering-seed-command.test.mjs @@ -0,0 +1,107 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import { createRequire } from "node:module"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import test from "node:test"; +import net from "node:net"; +import { MongoMemoryServer } from "mongodb-memory-server-core"; + +const require = createRequire(import.meta.url); +const projectRoot = fileURLToPath(new URL("../", import.meta.url)); +const loader = pathToFileURL(require.resolve("tsx")).href; +const seedScript = path.join(projectRoot, "scripts/seed-offerings.ts"); + +async function seed({ uri = "", args = ["--apply"], envFile, script = seedScript, recoveryUri } = {}) { + const cwd = await mkdtemp(path.join(tmpdir(), "coffee-seed-command-")); + try { + if (envFile) await writeFile(path.join(cwd, ".env.local"), envFile); + const env = { ...process.env, NODE_ENV: "development", TSX_TSCONFIG_PATH: path.join(projectRoot, "tsconfig.json") }; + delete env.__NEXT_PROCESSED_ENV; + if (recoveryUri) env.COFFEE_STORAGE_RECOVERY_URI = recoveryUri; + if (uri === undefined) delete env.MONGO_URI; + else env.MONGO_URI = uri; + return await new Promise((resolve, reject) => { + const child = spawn(process.execPath, ["--import", loader, script, ...args], { cwd, env, windowsHide: true }); + let output = ""; + for (const stream of [child.stdout, child.stderr]) stream.on("data", (chunk) => (output += chunk)); + let timedOut = false; + const deadline = setTimeout(() => { + timedOut = true; + child.kill(); + }, 40_000); + child.on("error", (error) => { + clearTimeout(deadline); + reject(error); + }); + child.on("close", (code) => { + clearTimeout(deadline); + if (timedOut) reject(new Error("Database command exceeded 40 seconds")); + else resolve({ code, output }); + }); + }); + } finally { + assert.equal(path.dirname(cwd), tmpdir()); + assert.ok(path.basename(cwd).startsWith("coffee-seed-command-")); + await rm(cwd, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 }); + } +} + +test("seed command requires deliberate application and valid configuration without leaking credentials", async () => { + for (const options of [ + { args: [] }, + { args: ["--typo"] }, + {}, + { uri: "mongodb://user:private-password@127.0.0.1/admin" }, + { uri: "mongodb://user:private-password@127.0.0.1/" }, + { uri: "{mongo-uri-here}" }, + ]) { + const result = await seed(options); + assert.equal(result.code, 1, result.output); + assert.match(result.output, /FAIL configuration/); + assert.doesNotMatch(result.output, /private-password|mongodb:\/\//); + } +}); + +test("seed command creates 16 items then reports zero inserts on a separate-process rerun", async () => { + const server = await MongoMemoryServer.create({ binary: { version: "7.0.24" } }); + try { + const uri = server.getUri("coffee_seed_command_test"); + const first = await seed({ uri, envFile: "MONGO_URI={mongo-uri-here}\n" }); + assert.equal(first.code, 0, first.output); + assert.match(first.output, /PASS seed: inserted=16/); + const second = await seed({ uri }); + assert.equal(second.code, 0, second.output); + assert.match(second.output, /PASS seed: inserted=0/); + } finally { + await server.stop(); + } +}); + +test("unreachable storage rejects list/create/seed, reports safe failure, and recovers on retry", async () => { + const listener = net.createServer(); + await new Promise((resolve) => listener.listen(0, "127.0.0.1", resolve)); + const port = listener.address().port; + await new Promise((resolve) => listener.close(resolve)); + const server = await MongoMemoryServer.create({ binary: { version: "7.0.24" } }); + try { + const result = await seed({ + uri: `mongodb://user:private-password@127.0.0.1:${port}/coffee_unreachable_test`, + recoveryUri: server.getUri("coffee_recovery_test"), + script: path.join(projectRoot, "tests/helpers/offering-storage-connection.ts"), + args: [], + }); + assert.equal(result.code, 0, result.output); + assert.match(result.output, /DATABASE_UNAVAILABLE/); + assert.match(result.output, /PASS recovery/); + assert.doesNotMatch(result.output, /private-password|mongodb:\/\/|MongoServer/); + const command = await seed({ uri: `mongodb://user:private-password@127.0.0.1:${port}/coffee_unreachable_test` }); + assert.equal(command.code, 1, command.output); + assert.match(command.output, /FAIL seed: database operation failed/); + assert.doesNotMatch(command.output, /private-password|mongodb:\/\/|MongoServer/); + } finally { + await server.stop(); + } +}); diff --git a/tests/offering-storage.test.ts b/tests/offering-storage.test.ts new file mode 100644 index 0000000..bccdaea --- /dev/null +++ b/tests/offering-storage.test.ts @@ -0,0 +1,177 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import mongoose from "mongoose"; +import { MongoMemoryServer } from "mongodb-memory-server-core"; +import { offeringStore } from "@/database/offeringStore"; +import type { CreateOfferingInput } from "@/types/offering"; + +test("offering storage persists public offerings in disposable local MongoDB", async (t) => { + const server = await MongoMemoryServer.create({ binary: { version: "7.0.24" } }); + process.env.MONGO_URI = server.getUri("coffee_storage_test"); + try { + await t.test("empty collection returns an empty list", async () => { + assert.deepEqual(await offeringStore.list(), []); + }); + + await t.test( + "create persists trimmed details and returns only public fields with generated string identity", + async () => { + const saved = await offeringStore.create({ + name: " Vanilla Latte ", + description: " Espresso with vanilla and milk ", + price: 5.75, + category: "Coffee", + }); + assert.match(saved.ID, /^[a-f0-9]{24}$/); + assert.deepEqual(saved, { + ID: saved.ID, + name: "Vanilla Latte", + description: "Espresso with vanilla and milk", + price: 5.75, + category: "Coffee", + specialOffer: false, + }); + assert.deepEqual(await offeringStore.list(), [saved]); + }, + ); + + await t.test("list sorts by name then generated string ID, including duplicate names", async () => { + const zulu = await offeringStore.create({ name: "Zulu", description: "Last", price: 0, category: "Tea" }); + const alpha = await offeringStore.create({ name: "Alpha", description: "First", price: 1, category: "Tea" }); + // Arrange equal-name documents in reverse ID order to exercise the tie-breaker. + await mongoose.connection.collection("offerings").insertMany([ + { ...alpha, ID: undefined, _id: new mongoose.Types.ObjectId("000000000000000000000002"), name: "Aardvark" }, + { ...alpha, ID: undefined, _id: new mongoose.Types.ObjectId("000000000000000000000001"), name: "Aardvark" }, + ]); + const list = await offeringStore.list(); + assert.deepEqual( + list.map((offering) => offering.name), + ["Aardvark", "Aardvark", "Alpha", "Vanilla Latte", "Zulu"], + ); + assert.deepEqual( + list.slice(0, 2).map((offering) => offering.ID), + ["000000000000000000000001", "000000000000000000000002"], + ); + assert.equal(list.at(-1)?.ID, zulu.ID); + }); + + await t.test("model guards reject invalid details without persisting them", async () => { + const valid: CreateOfferingInput = { name: "Guard", description: "Valid", price: 1, category: "Tea" }; + const before = await offeringStore.list(); + for (const changes of [ + { name: " " }, + { name: "n".repeat(101) }, + { description: " " }, + { description: "d".repeat(501) }, + { category: "Other" }, + { price: -1 }, + { price: Number.NaN }, + { price: Number.POSITIVE_INFINITY }, + { price: 1.001 }, + { specialOffer: null }, + { specialOffer: "maybe" }, + ]) { + await assert.rejects(offeringStore.create({ ...valid, ...changes } as CreateOfferingInput)); + } + assert.deepEqual(await offeringStore.list(), before); + }); + + await t.test( + "zero price, boundary text lengths, floating-point cents, and explicit special flags are supported", + async () => { + const zero = await offeringStore.create({ + name: "n".repeat(100), + description: "d".repeat(500), + price: 0, + category: "Smoothie", + specialOffer: true, + }); + const decimal = await offeringStore.create({ + name: "Decimal", + description: "Floating-point cents", + price: 0.1 + 0.2, + category: "Tea", + specialOffer: false, + }); + assert.equal(zero.price, 0); + assert.equal(zero.specialOffer, true); + assert.equal(decimal.specialOffer, false); + assert.equal(decimal.price, 0.1 + 0.2); + assert.ok((await offeringStore.list()).some((offering) => offering.ID === zero.ID)); + }, + ); + + await t.test( + "seed inserts 16 starting offerings and reruns preserve IDs, edits, and unrelated records", + async () => { + const { seedOfferings } = await import("@/database/seedOffering"); + const unrelated = await offeringStore.list(); + assert.equal(await seedOfferings(), 16); + const seeded = (await offeringStore.list()).filter( + (offering) => !unrelated.some((item) => item.ID === offering.ID), + ); + assert.equal(seeded.length, 16); + assert.deepEqual( + seeded.filter((offering) => offering.specialOffer).map((offering) => offering.name), + ["Golden Hour Cold Brew", "Hojicha Latte", "Pumpkin Spice Latte"], + ); + assert.deepEqual( + seeded.map((offering) => offering.name), + [ + "Cafe au Lait", + "Cappuccino", + "Chai Latte", + "Chamomile Tea", + "Cortado", + "English Breakfast Tea", + "Espresso", + "Fruit Smoothie", + "Golden Hour Cold Brew", + "Hojicha Latte", + "Iced Hibiscus Berry", + "Latte", + "London Fog", + "Matcha", + "Mocha", + "Pumpkin Spice Latte", + ], + ); + for (const offering of seeded) { + assert.match(offering.ID, /^[a-f0-9]{24}$/); + assert.deepEqual(Object.keys(offering).sort(), [ + "ID", + "category", + "description", + "name", + "price", + "specialOffer", + ]); + } + + const cappuccino = seeded.find((offering) => offering.name === "Cappuccino")!; + // Simulate an operator edit, including rename. Identity must not depend on the name. + await mongoose.connection + .collection("offerings") + .updateOne( + { _id: new mongoose.Types.ObjectId(cappuccino.ID) }, + { $set: { name: "Operator's Cappuccino", description: "Keep my edit", price: 7.25, specialOffer: true } }, + ); + const beforeRerun = await offeringStore.list(); + assert.equal(await seedOfferings(), 0); + assert.deepEqual(await offeringStore.list(), beforeRerun); + assert.equal(await seedOfferings(), 0); + assert.deepEqual(await offeringStore.list(), beforeRerun); + for (const offering of unrelated) { + assert.deepEqual( + (await offeringStore.list()).find((item) => item.ID === offering.ID), + offering, + ); + } + }, + ); + } finally { + await mongoose.disconnect(); + await server.stop(); + delete process.env.MONGO_URI; + } +}); From ba73b763eb5a95ddb8b7385668ac9bb115ba1618 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Sun, 11 Oct 2026 09:59:25 -0700 Subject: [PATCH 25/29] test: add Vitest foundation and customer-page handoff --- .github/workflows/ci.yml | 14 +- README.md | 8 + docs/milestone-3-evidence.md | 28 + docs/project-setup.md | 4 +- package-lock.json | 1903 ++++++++++++++++++++++++---- package.json | 13 +- tests/unit/README.md | 59 + tests/unit/customer-pages.test.tsx | 21 + tests/unit/helpers/offerings.ts | 18 + vitest.config.mts | 16 + 10 files changed, 1842 insertions(+), 242 deletions(-) create mode 100644 docs/milestone-3-evidence.md create mode 100644 tests/unit/README.md create mode 100644 tests/unit/customer-pages.test.tsx create mode 100644 tests/unit/helpers/offerings.ts create mode 100644 vitest.config.mts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0afb38c..2cc7d0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,15 +34,19 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install dependencies - run: npm install + run: npm ci + + - name: Lint + run: npm run lint + + - name: Check types + run: npm run typecheck - name: Dry-run build run: npm run build env: - # These env variables must be set in the repository's secrets - # - Settings -> Secrets and variables -> New repository secret - MONGO_URI: ${{ secrets.MONGO_URI }} - # Add additional environment variables here + # Routine checks use controlled responses or disposable local MongoDB. + MONGO_URI: "" - name: Run tests run: npm test diff --git a/README.md b/README.md index c4dbd1d..3aa284c 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,14 @@ Learning developers start with the first two guides. Coding agents start with `A 3. [Project setup](docs/project-setup.md) — tools already configured in this repository. 4. [AGENTS.md](AGENTS.md) — how a coding agent should change this repo. +## Customer-page testing: start here + +The [Vitest handoff](tests/unit/README.md) explains the runnable customer-page +starter on `main`, Vedika's three unimplemented cases for #35, and verification +commands. Record personal contributions in the +[Milestone 3 evidence table](docs/milestone-3-evidence.md). Customer-page testing +can start before PR #31 merges; release-only tests remain on that branch. + ## Milestone 2: start your assignment Open the [independent developer plan](docs/milestone-2-parallel-work.md), find your issue, and read its acceptance criteria. Every assignment can be built, tested, reviewed, and merged without waiting for another developer's issue to finish. diff --git a/docs/milestone-3-evidence.md b/docs/milestone-3-evidence.md new file mode 100644 index 0000000..f657209 --- /dev/null +++ b/docs/milestone-3-evidence.md @@ -0,0 +1,28 @@ +# Milestone 3 release evidence + +## Test foundation + +Current `main` includes Vitest configuration, controlled component-test helpers, +and one loading example in `tests/unit/customer-pages.test.tsx`. That example is +agent-written foundation coverage, not a developer's personal contribution. +Vedika's three cases for #35 remain for her to write. See the +[test handoff](../tests/unit/README.md). + +Release features and their dependent test suites remain in PR #31 until integrated. +Controlled responses and disposable local databases do not prove live deployment +or Atlas persistence. Keep unverified release results pending. + +## Developer evidence + +| Developer | PR | Three personally authored test names | What each test protects | Browser or other evidence | Explained and reviewed | +| --------- | ------- | ------------------------------------ | ----------------------- | ------------------------- | ---------------------- | +| Brian | Pending | Pending | Pending | Pending | Pending | +| Jean | Pending | Pending | Pending | Pending | Pending | +| Naomi | Pending | Pending | Pending | Pending | Pending | +| Vedika | Pending | Pending | Pending | Pending | Pending | +| Aditi | Pending | Pending | Pending | Pending | Pending | +| Sofie | Pending | Pending | Pending | Pending | Pending | + +Record browser evidence with route, viewport, commit, result, and screenshot link. +Keep credentials and populated environment files out of evidence. Carry this +contribution record forward when PR #31 adds its release verification matrix. diff --git a/docs/project-setup.md b/docs/project-setup.md index b5c3fc8..d90660d 100644 --- a/docs/project-setup.md +++ b/docs/project-setup.md @@ -10,5 +10,5 @@ These tools are already configured. You do not need to add them before writing a 6. **ESLint and Prettier.** ESLint extends `next/core-web-vitals` and reports Prettier issues as errors. Prettier uses a print width of 120. `npm run lint` runs the check. 7. **Pre-commit hook.** Husky runs lint-staged, which formats staged files and runs ESLint on JavaScript and TypeScript. 8. **Issue and pull request templates** in `.github`. -9. **Continuous integration.** `.github/workflows/ci.yml` runs on pushes and pull requests to `main` and `develop`. The job installs dependencies, runs `npm run build`, and runs `npm test` on Node 18, 20, and 22. The build step receives `MONGO_URI` from the repository secret of the same name. -10. **Tests.** `npm test` uses Node's built-in test runner to exercise the offerings endpoint over HTTP. It starts its own Next.js server with separate build output and no Atlas credentials. `npm run typecheck` checks TypeScript. See [the Offering contract](./offering-api-contract.md) for coverage and future persistence checks. +9. **Continuous integration.** `.github/workflows/ci.yml` runs on pushes and pull requests to `main` and `develop`. The job installs locked dependencies, checks lint and types, builds without Atlas credentials, and runs both test suites on Node 18, 20, and 22. +10. **Tests.** `npm test` runs Vitest unit/component tests and the existing Node regression suite. `npm run test:unit` selects `tests/unit/**/*.test.{ts,tsx}`; the customer-page starter uses jsdom and controlled fetch responses. Node regressions exercise HTTP, component behavior, database checks, storage, and seeding using isolated tooling. `npm run preflight` runs lint, typecheck, both suites, and the build. See the [test handoff](../tests/unit/README.md) and [Offering contract](./offering-api-contract.md). diff --git a/package-lock.json b/package-lock.json index 9f0da71..4303a96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "react-dom": "^18" }, "devDependencies": { + "@testing-library/dom": "^10.4.2", + "@testing-library/react": "^16.3.3", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -28,7 +30,8 @@ "mongodb-memory-server-core": "^10.4.3", "prettier": "^3", "tsx": "^4.23.15", - "typescript": "^5" + "typescript": "^5", + "vitest": "^3.2.7" } }, "node_modules/@asamuzakjp/css-color": { @@ -45,6 +48,41 @@ "lru-cache": "^10.4.3" } }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", + "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -737,6 +775,13 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", @@ -745,6 +790,26 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@next/env": { "version": "14.2.15", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.15.tgz", @@ -960,132 +1025,611 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.5.tgz", + "integrity": "sha512-MrfgRQSpD5hkkRg6XI16B6EDLb+6+xp8xje6bpdSwkYw+qxrBKbUVrRAQu9h82UT+QumSIVQ8HXsXjPd2dlhqw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", - "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", - "dev": true + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.5.tgz", + "integrity": "sha512-+uu2p/R9Tzn8GpoQyX1pAo5dmKT1DhXevfaCzqzmPFhnLjHLWw7uWkaVGlxmhwYoRiYYhsGfRcD5cTBpL7rgSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.5.tgz", + "integrity": "sha512-17dys+YEmzwzzFVHQAVDONg5FALXY9kd99HFEpfk4gJVHYxoh5bGea3D4t1fXXnwkTcOe25wPyd6nWS0I8IdLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", - "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" - } + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.5.tgz", + "integrity": "sha512-wg0zQEKkEvFZf1UaaX1UELOoPs//OSKUG7D+yITuqOsAvza7LvHTv5Wu4nmMfzNG7EYI2jp2PJC7Id6OETmsYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.5.tgz", + "integrity": "sha512-CoJmWcpEMuwin+TZ/ev/H9uDyF/uxUKQSWXdmIPn/UBx/uGV9+4ebMp0FwnlhWR9PE1AdzYsKf3bZevhif38+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@types/node": { - "version": "20.16.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.12.tgz", - "integrity": "sha512-LfPFB0zOeCeCNQV3i+67rcoVvoN5n0NVuR2vLG0O5ySQMgchuZlC4lgz546ZOJyDtj5KIgOxy+lacOimfqZAIA==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.5.tgz", + "integrity": "sha512-PxZnLAaTUAL5xG0W2BXLeYPKrK2b3LIASV2/e/Kf7PZvSLI1iAQIm9B9aJD+YyFIT7hc6qaMV3+OOO35R28LfA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "undici-types": "~6.19.2" - } + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.5.tgz", + "integrity": "sha512-uPo3IvnONjhbFXwWgroBeFwOicuhT7ZwQrnzkzmOtBJzeQbkUJUE1SJDfDI31ijQ5+ZUIA6sPrdUMxTSnxUWAg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/react": { - "version": "18.3.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", - "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.5.tgz", + "integrity": "sha512-ywI8Jyuc4LyHYTjC0mw4yA1ulvZBkHusdpWnoiEBsmBuHvSnt90btTBGCZ+s/2y+YNNfVAEKy33nPo5UI9zTmg==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.5.tgz", + "integrity": "sha512-tmuXJoQ/3ovSr637aykZMdQteU/CB2APAz2CrvsbibuoHZi1BS1Db13cVViBdEARGwXCLn1DSJ8wsjoThLJPGA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@types/react": "*" - } + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.5.tgz", + "integrity": "sha512-5FMjSYNcUOHx7oW8Qu9KB6jGPpvF/8sG7+BDHwU/Ihh1/6FMJ9ySmsFG4tJ1qbNRuP8OLzCBDso4sF0mQaLuFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "dependencies": { - "@types/webidl-conversions": "*" - } + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.5.tgz", + "integrity": "sha512-K8ZJ+neLDbUkIrL8dqF/5LtuVCrKwo27kREgOlxjBEjlOXwHvDnHLU8Z/UKv/B49qi7e8hsSDOyoJwhwqhvX4w==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", - "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.5.tgz", + "integrity": "sha512-NlVxsq7PFPv0DBKGuNWRDKixQYQCjM53zl04mxEcKMBmJbCBIUm1JdPfIWDRHyOOFzzYPmLQzfeEwh4716ayQg==", + "cpu": [ + "loong64" + ], "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/type-utils": "8.10.0", - "@typescript-eslint/utils": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.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.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@typescript-eslint/parser": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", - "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.5.tgz", + "integrity": "sha512-ARhRHJYs5eCFet5azfrHD97vOkD8OzNW2RSMlvo5CcBqXaTOLfp0OHDtUQh7/K5AL5fUYoFp2fgC9KDg4vVYMQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.5.tgz", + "integrity": "sha512-atSgreM5C7jpxd9aEXN+6vJ5wTd05iYw+S3FiQpw7VsoEl8B90llbq35ARON+GOHj5QlVQEq2vOKWCHTob/BSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.5.tgz", + "integrity": "sha512-vmlmwBFGnjVWxSRoVabNkyYuscSmg52Pd0fM8jEbNT3Jmka4A6HcB84x4uAmuumznWfBVaxKOqU6jbEgomWKCA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.5.tgz", + "integrity": "sha512-hhJrP6S9WkbsbcHgUkvCBS5Cn8IdsG8XjlLZyxjnv9+NdoYfbbZE4TWJYr8apdaj1M5t5Y1id2StnEslxpNFyA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.5.tgz", + "integrity": "sha512-NNVCyN9pGrwFmQa4bf+Cnf5aVH3g6ZpnlOg1Cs/H9swIHT9bRixijfzUqyEIWm1WtiAuR1LJ+rJH6zpBm8kgQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.5.tgz", + "integrity": "sha512-dR9nd6OKnvSS60Ifz/L7Mj5zzYyoqyRn2NwTuaOONKaa3xsVGXyp8YusMbvFyEOR94DxnwgcbgsFqkHd29ZSzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.5.tgz", + "integrity": "sha512-1tzsMjaqhsh3bAO6SJiJqh8hIr9sSPRd+i2vPPxJHod8/XYnf9TEnK0z/u3r1z1AkI347X0KbRSg9dWcxeGahw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.5.tgz", + "integrity": "sha512-01mWMQblKoXXsmk28KXtpbA96w7g/aOWUoV3AC16fdHdqkykGalqyyM+5u8M7uP5/MEMiiPv69Hv7iv/hEH/dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.5.tgz", + "integrity": "sha512-trdnMPb/DNm4V/CgsaoGgr90HfQQ071wfK0KoIs4YpLvjBgVQGXhGabg2Z6nK4EcYNilrb9ZZPcsP6+zpJIdxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.5.tgz", + "integrity": "sha512-x2LxZlfdfsw99T6/qb9EqE/Qi84JWzibjyOJyhJsgFJy6CafaTiqIXCCnslCGrV5XWNh7fwrS3FbRENcuKnooA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.5.tgz", + "integrity": "sha512-/Qd/9aDQIo0+jmItgBmPIs8195YVbDxRRApn47GyufgbutCDOh7eP5vSKKlI4QNFeaI+wKQAkSMsPnF3EuMpaA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.5.tgz", + "integrity": "sha512-ngUJ/XvOWrqbd2IMsfXfh2xXFLTP3sY7sCKvxbGvYdBZ1KmXFd3ewDZ9n3/GNzAUgzRuhLT/UvJRivbNoAQRBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.5.tgz", + "integrity": "sha512-IhV6ew/DuzUWwHaYmlh7MrPCV3lKFUq/amTEwysi4r/mwNPAEfAMh5dYWeTSPTeMy2pFHGMEWYtPkHzfX3tG5A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", + "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", + "dev": true + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz", + "integrity": "sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/react": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz", + "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.16.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.12.tgz", + "integrity": "sha512-LfPFB0zOeCeCNQV3i+67rcoVvoN5n0NVuR2vLG0O5ySQMgchuZlC4lgz546ZOJyDtj5KIgOxy+lacOimfqZAIA==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", + "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/type-utils": "8.10.0", + "@typescript-eslint/utils": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.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.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", + "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.10.0", + "@typescript-eslint/types": "8.10.0", + "@typescript-eslint/typescript-estree": "8.10.0", + "@typescript-eslint/visitor-keys": "8.10.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1251,6 +1795,94 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", @@ -1502,6 +2134,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -1698,6 +2340,16 @@ "node": ">=10.16.0" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -1758,6 +2410,23 @@ } ] }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1774,6 +2443,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -2061,6 +2740,16 @@ "dev": true, "license": "MIT" }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -2133,6 +2822,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2145,6 +2844,13 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2321,6 +3027,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -2886,6 +3599,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2934,6 +3657,16 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4527,12 +5260,39 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, "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 }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -4788,15 +5548,16 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5188,6 +5949,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -5365,6 +6143,41 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.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/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5591,6 +6404,52 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.64.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.5.tgz", + "integrity": "sha512-EAP5ZHcz2ijbgCWwPkpI7zh6bfsri2Ez5FtCy2dCp1k62duXzfG7DLGGh7RU2QQXWwavT+u4qpvDF/TWOiiGTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.64.5", + "@rollup/rollup-android-arm64": "4.64.5", + "@rollup/rollup-darwin-arm64": "4.64.5", + "@rollup/rollup-darwin-x64": "4.64.5", + "@rollup/rollup-freebsd-arm64": "4.64.5", + "@rollup/rollup-freebsd-x64": "4.64.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.64.5", + "@rollup/rollup-linux-arm-musleabihf": "4.64.5", + "@rollup/rollup-linux-arm64-gnu": "4.64.5", + "@rollup/rollup-linux-arm64-musl": "4.64.5", + "@rollup/rollup-linux-loong64-gnu": "4.64.5", + "@rollup/rollup-linux-loong64-musl": "4.64.5", + "@rollup/rollup-linux-ppc64-gnu": "4.64.5", + "@rollup/rollup-linux-ppc64-musl": "4.64.5", + "@rollup/rollup-linux-riscv64-gnu": "4.64.5", + "@rollup/rollup-linux-riscv64-musl": "4.64.5", + "@rollup/rollup-linux-s390x-gnu": "4.64.5", + "@rollup/rollup-linux-x64-gnu": "4.64.5", + "@rollup/rollup-linux-x64-musl": "4.64.5", + "@rollup/rollup-openbsd-x64": "4.64.5", + "@rollup/rollup-openharmony-arm64": "4.64.5", + "@rollup/rollup-win32-arm64-msvc": "4.64.5", + "@rollup/rollup-win32-ia32-msvc": "4.64.5", + "@rollup/rollup-win32-x64-gnu": "4.64.5", + "@rollup/rollup-win32-x64-msvc": "4.64.5", + "fsevents": "~2.3.2" + } + }, "node_modules/rrweb-cssom": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", @@ -5773,6 +6632,13 @@ "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -5814,9 +6680,10 @@ } }, "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==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -5829,6 +6696,20 @@ "memory-pager": "^1.0.2" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -6101,6 +6982,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -6242,11 +7143,103 @@ } } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/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/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz", + "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, "node_modules/tldts": { "version": "6.1.86", @@ -6328,169 +7321,598 @@ "strip-bom": "^3.0.0" } }, - "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/tsx": { - "version": "4.23.15", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", - "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "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/tsx": { + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "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, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/vite-node/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/vite-node/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite-node/node_modules/postcss": { + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", + "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.19", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", + "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "~0.28.0" + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { - "tsx": "dist/cli.mjs" + "vite": "bin/vite.js" }, "engines": { - "node": ">=18.0.0" + "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", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.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 + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, - "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==", + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, - "engines": { - "node": ">= 0.4" + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "node_modules/vitest/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "node_modules/vitest/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, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "node_modules/vitest/node_modules/postcss": { + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "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.19", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" }, "engines": { - "node": ">=14.17" + "node": "^10 || ^12 || >=14" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/vitest/node_modules/vite": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", + "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true - }, - "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, - "dependencies": { - "punycode": "^2.1.0" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.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 + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, "node_modules/w3c-xmlserializer": { @@ -6644,6 +8066,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index e20cc91..143a14b 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,15 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint", + "lint": "next lint --dir src --dir tests/unit --file vitest.config.mts", "lint:fix": "next lint --fix", "format": "prettier --write .", "preview:offerings": "node scripts/offering-preview-server.mjs", - "test": "node --test --import tsx tests/offerings-http.test.mjs tests/database-checker.test.mjs tests/offering-form.test.mjs src/lib/offerings.test.ts tests/special-offering-control.test.mjs tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", + "test": "npm run test:unit && npm run test:regression", + "test:unit": "vitest run", + "test:watch": "vitest", + "test:regression": "node --test --import tsx tests/offerings-http.test.mjs tests/database-checker.test.mjs tests/offering-form.test.mjs src/lib/offerings.test.ts tests/special-offering-control.test.mjs tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", + "preflight": "npm run lint && npm run typecheck && npm test && npm run build", "test:storage": "node --test --import tsx tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", "db:seed": "node --import tsx scripts/seed-offerings.ts", "test:db": "node --test tests/database-checker.test.mjs", @@ -27,6 +31,8 @@ "react-dom": "^18" }, "devDependencies": { + "@testing-library/dom": "^10.4.2", + "@testing-library/react": "^16.3.3", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -41,6 +47,7 @@ "mongodb-memory-server-core": "^10.4.3", "prettier": "^3", "tsx": "^4.23.15", - "typescript": "^5" + "typescript": "^5", + "vitest": "^3.2.7" } } diff --git a/tests/unit/README.md b/tests/unit/README.md new file mode 100644 index 0000000..e06cdfc --- /dev/null +++ b/tests/unit/README.md @@ -0,0 +1,59 @@ +# Unit and component test handoff + +Vitest runs `tests/unit/**/*.test.ts` and `tests/unit/**/*.test.tsx`. The existing +`.mjs` regression files still use Node's test runner; keep their imports and +coverage intact. + +## Vedika: issue #35 + +Start from updated `main`. You can complete this customer-page assignment without +waiting for PR #31. Extend `customer-pages.test.tsx`, which renders the shared +`OfferingsPage` component used by `/menu` and `/specials`. + +The file contains one foundation loading test, browser setup, and cleanup. Vitest +restores stubbed globals after each test. Optional sample data and a JSON response +helper are in `helpers/offerings.ts`. None of your assigned Vitest cases has been +implemented for you. Existing Node regressions cover related UI states; keep them +and add your own rendered assertions in the Vitest file. + +Personally write three new tests: + +1. Specials shows its empty feedback when the response contains offerings but none + have `specialOffer: true`. +2. A rejected network request shows an error and does not show the empty-menu feedback. +3. An unsuccessful API response with an unreadable error body shows useful fallback feedback. + +Use controlled fetch responses. Render the component with the appropriate props +and inspect visible feedback. Async requests require waiting for the resulting UI. +Fix any related defects your tests expose. Do not copy the loading example and +claim it as a contribution, or replace the assigned cases with skipped tests. + +Also check Home, About, Contact, Menu, Specials, and navigation at desktop/mobile +widths and with a keyboard. Capture focus, long-text, and overflow evidence as +required by [issue #35](https://github.com/BrianLiDesign/coffee-shop/issues/35). + +## Commands + +- Install the locked dependencies: `npm ci`. +- Run only customer-page tests: `npm run test:unit -- tests/unit/customer-pages.test.tsx`. +- Watch unit/component tests: `npm run test:watch`. +- Run all checks before submitting: `npm run preflight`. + +Open your own reviewed PR into `main`. Record its link, your three test names, +what each protects, and browser evidence in +[`docs/milestone-3-evidence.md`](../../docs/milestone-3-evidence.md). +Controlled responses verify UI behavior; they do not prove live database persistence. + +## Other developer assignments and release integration + +This foundation does not copy the release branch's completed unit suites into +`main`. Its handler, replacement management component, and live acceptance runner +are release-only modules until integrated. Their tests stay with PR #31, and their +owners should follow their live issues for when to start. Compatible baseline +coverage on current `main` is the loading example above; all existing Node +regressions remain enabled. + +When reconciling PR #31, preserve this handoff and each developer's contributed +cases. Keep agent-written reference tests distinct from personal contributions. +Reconcile the Vitest version/configuration with the release runtime, and retain +both unit and regression commands, including storage and seed coverage from `main`. diff --git a/tests/unit/customer-pages.test.tsx b/tests/unit/customer-pages.test.tsx new file mode 100644 index 0000000..3fb8938 --- /dev/null +++ b/tests/unit/customer-pages.test.tsx @@ -0,0 +1,21 @@ +// @vitest-environment jsdom +import { afterEach, expect, it, vi } from "vitest"; +import { cleanup, render, screen } from "@testing-library/react"; +import OfferingsPage from "@/components/OfferingsPage"; + +afterEach(cleanup); + +const props = { title: "Menu", intro: "Our drinks", emptyMessage: "No offerings yet." }; + +// Foundation example only. This is not one of Vedika's three contributions. +it("announces loading while a request is pending", () => { + vi.stubGlobal( + "fetch", + vi.fn(() => new Promise(() => {})), + ); + render(); + expect(screen.getByRole("status").textContent).toBe("Loading menu..."); +}); + +// Issue #35: write three NEW cases below. See ./README.md for the requirements. +// Leave the example above in place; do not count it as a personal contribution. diff --git a/tests/unit/helpers/offerings.ts b/tests/unit/helpers/offerings.ts new file mode 100644 index 0000000..220bf5a --- /dev/null +++ b/tests/unit/helpers/offerings.ts @@ -0,0 +1,18 @@ +import type { Offering } from "@/types/offering"; + +/** Sample data for controlled component tests, never a runtime fallback. */ +export const sampleOffering: Offering = { + ID: "sample-latte", + name: "Latte", + description: "Espresso and milk", + price: 4.25, + category: "Coffee", + specialOffer: false, +}; + +export function jsonResponse(body: unknown, status = 200): Response { + return new Response(JSON.stringify(body), { + status, + headers: { "Content-Type": "application/json" }, + }); +} diff --git a/vitest.config.mts b/vitest.config.mts new file mode 100644 index 0000000..0989b58 --- /dev/null +++ b/vitest.config.mts @@ -0,0 +1,16 @@ +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) } }, + // Next preserves JSX for its compiler; component tests need automatic JSX. + esbuild: { jsx: "automatic" }, + test: { + include: ["tests/unit/**/*.test.{ts,tsx}"], + environment: "node", + restoreMocks: true, + unstubEnvs: true, + unstubGlobals: true, + testTimeout: 15000, + }, +}); From 9054f88dde8b1ed8a7289f014d9ebfd5d8465a88 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Sun, 11 Oct 2026 10:05:44 -0700 Subject: [PATCH 26/29] fix: pin Node 18 compatible Vitest dependencies --- package-lock.json | 953 ++++++++++++++++++++++++++++++++-------------- package.json | 3 +- 2 files changed, 661 insertions(+), 295 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4303a96..93e1e34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "devDependencies": { "@testing-library/dom": "^10.4.2", "@testing-library/react": "^16.3.3", - "@types/node": "^20", + "@types/node": "^20.19.43", "@types/react": "^18", "@types/react-dom": "^18", "esbuild": "^0.28.2", @@ -31,6 +31,7 @@ "prettier": "^3", "tsx": "^4.23.15", "typescript": "^5", + "vite": "^6.4.4", "vitest": "^3.2.7" } }, @@ -1537,12 +1538,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.16.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.12.tgz", - "integrity": "sha512-LfPFB0zOeCeCNQV3i+67rcoVvoN5n0NVuR2vLG0O5ySQMgchuZlC4lgz546ZOJyDtj5KIgOxy+lacOimfqZAIA==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/prop-types": { @@ -7472,10 +7474,11 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" }, "node_modules/uri-js": { "version": "4.4.1", @@ -7486,126 +7489,25 @@ "punycode": "^2.1.0" } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-node/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/vite-node/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/vite-node/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vite-node/node_modules/postcss": { - "version": "8.5.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", - "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", - "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.19", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/vite-node/node_modules/vite": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", - "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", + "node_modules/vite": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.4.tgz", + "integrity": "sha512-xGjU/SyjeNyb0E4KxXBvYz/1C+3JXuujvf2hXNOtnlcE+58o5ajd9N2/n2VhkiJvpCP2qQasnUpx5PXNeI+p1Q==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -7614,14 +7516,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", - "less": "^4.0.0", + "less": "*", "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -7662,107 +7564,30 @@ } } }, - "node_modules/vitest": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", - "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.7", - "@vitest/mocker": "3.2.7", - "@vitest/pretty-format": "^3.2.7", - "@vitest/runner": "3.2.7", - "@vitest/snapshot": "3.2.7", - "@vitest/spy": "3.2.7", - "@vitest/utils": "3.2.7", - "chai": "^5.2.0", + "cac": "^6.7.14", "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", + "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { - "vitest": "vitest.mjs" + "vite-node": "vite-node.mjs" }, "engines": { "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.7", - "@vitest/ui": "3.2.7", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/@vitest/mocker": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", - "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.7", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } } }, - "node_modules/vitest/node_modules/debug": { + "node_modules/vite-node/node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", @@ -7780,55 +7605,539 @@ } } }, - "node_modules/vitest/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=18" } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=18" } }, - "node_modules/vitest/node_modules/postcss": { - "version": "8.5.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", - "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", - "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" - } + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/vite/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/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", + "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": { @@ -7840,81 +8149,137 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/vitest/node_modules/vite": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", - "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" }, "bin": { - "vite": "bin/vite.js" + "vitest": "vitest.mjs" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.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" + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" }, "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { + "@edge-runtime/vm": { "optional": true }, - "less": { + "@types/debug": { "optional": true }, - "lightningcss": { + "@types/node": { "optional": true }, - "sass": { + "@vitest/browser": { "optional": true }, - "sass-embedded": { + "@vitest/ui": { "optional": true }, - "stylus": { + "happy-dom": { "optional": true }, - "sugarss": { + "jsdom": { "optional": true - }, - "terser": { + } + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { "optional": true }, - "tsx": { + "vite": { "optional": true - }, - "yaml": { + } + } + }, + "node_modules/vitest/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { "optional": true } } }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", diff --git a/package.json b/package.json index 143a14b..b7a904e 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "devDependencies": { "@testing-library/dom": "^10.4.2", "@testing-library/react": "^16.3.3", - "@types/node": "^20", + "@types/node": "^20.19.43", "@types/react": "^18", "@types/react-dom": "^18", "esbuild": "^0.28.2", @@ -48,6 +48,7 @@ "prettier": "^3", "tsx": "^4.23.15", "typescript": "^5", + "vite": "^6.4.4", "vitest": "^3.2.7" } } From b7ba47c72ac05d5eda9fc0060628b1e905de3570 Mon Sep 17 00:00:00 2001 From: Jeangirlbell Date: Sun, 11 Oct 2026 10:22:27 -0700 Subject: [PATCH 27/29] feat: complete offering handlers and validation (#16) --- src/app/api/offerings/handlers.ts | 82 +++++++++++++++++++++++++++++++ src/app/api/offerings/route.ts | 81 ++---------------------------- src/lib/offering-store.ts | 17 +++++-- tests/offerings-handlers.test.mjs | 60 ++++++++++++++++++++++ 4 files changed, 158 insertions(+), 82 deletions(-) create mode 100644 src/app/api/offerings/handlers.ts create mode 100644 tests/offerings-handlers.test.mjs diff --git a/src/app/api/offerings/handlers.ts b/src/app/api/offerings/handlers.ts new file mode 100644 index 0000000..f686e53 --- /dev/null +++ b/src/app/api/offerings/handlers.ts @@ -0,0 +1,82 @@ +import { NextResponse } from "next/server"; +import { CreateOfferingInput } from "@/types/offering"; +import { OfferingStore } from "@/lib/offering-store"; + +export async function getHandler(store: OfferingStore) { + try { + const data = await store.list(); + return NextResponse.json(data, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: "Service Unavailable", message: "Storage failure" }, { status: 503 }); + } +} + +export async function postHandler(store: OfferingStore, request: Request) { + const contentType = request.headers.get("content-type"); + if (!contentType || !contentType.includes("application/json")) { + return NextResponse.json( + { error: "Unsupported Media Type", message: "Content-Type must be application/json" }, + { status: 415 }, + ); + } + + let body: any; + try { + body = await request.json(); + } catch (error) { + return NextResponse.json({ error: "Bad Request", message: "Malformed JSON" }, { status: 400 }); + } + + const validationResult = validateInput(body); + if (!validationResult.valid) { + return NextResponse.json({ error: "Bad Request", message: validationResult.message }, { status: 400 }); + } + + try { + const created = await store.create(validationResult.data as CreateOfferingInput); + return NextResponse.json(created, { status: 201 }); + } catch (error) { + return NextResponse.json({ error: "Service Unavailable", message: "Storage failure" }, { status: 503 }); + } +} + +function validateInput(body: any): { valid: boolean; message?: string; data?: Partial } { + if (typeof body !== "object" || body === null) { + return { valid: false, message: "Request body must be an object" }; + } + + const allowedKeys = ["name", "description", "price", "category", "specialOffer"]; + const keys = Object.keys(body); + + for (const key of keys) { + if (!allowedKeys.includes(key)) { + return { valid: false, message: `Unknown or identity field: ${key}` }; + } + } + + if (typeof body.name !== "string" || body.name.trim().length === 0 || body.name.trim().length > 30) { + return { valid: false, message: "Name must be a non-empty string with max length 30" }; + } + + if (body.description !== undefined && (typeof body.description !== "string" || body.description.length > 200)) { + return { valid: false, message: "Description must be a string with max length 200" }; + } + + if (typeof body.price !== "number" || body.price < 0) { + return { valid: false, message: "Price must be a non-negative number" }; + } + if (!/^\d+(\.\d{1,2})?$/.test(body.price.toString())) { + return { valid: false, message: "Price must have at most two decimal places" }; + } + + const allowedCategories = ["Coffee", "Tea", "Smoothie"]; + if (typeof body.category !== "string" || !allowedCategories.includes(body.category)) { + return { valid: false, message: "Invalid category" }; + } + + if (body.specialOffer !== undefined && typeof body.specialOffer !== "boolean") { + return { valid: false, message: "Special offer must be a boolean" }; + } + + return { valid: true, data: body as CreateOfferingInput }; +} diff --git a/src/app/api/offerings/route.ts b/src/app/api/offerings/route.ts index 7e9ce83..c6e8bcc 100644 --- a/src/app/api/offerings/route.ts +++ b/src/app/api/offerings/route.ts @@ -1,85 +1,12 @@ -import { NextResponse } from "next/server"; import { InMemoryOfferingStore } from "@/lib/offering-store"; -import { CreateOfferingInput } from "@/types/offering"; +import { getHandler, postHandler } from "./handlers"; -const store = new InMemoryOfferingStore(); +const publicStore = new InMemoryOfferingStore(); export async function GET() { - try { - const data = await store.list(); - return NextResponse.json(data, { status: 200 }); - } catch (error) { - return NextResponse.json({ error: "Bad Request", message: "Malformed JSON" }, { status: 400 }); - } + return getHandler(publicStore); } export async function POST(request: Request) { - const contentType = request.headers.get("content-type"); - if (!contentType || !contentType.includes("application/json")) { - return NextResponse.json( - { error: "Unsupported Media Type", message: "Content-Type must be application/json" }, - { status: 415 }, - ); - } - - // 2. 解析 JSON (400) - let body: any; - try { - body = await request.json(); - } catch (error) { - return NextResponse.json({ error: "Bad Request", message: "Malformed JSON" }, { status: 400 }); - } - - const validationResult = validateInput(body); - if (!validationResult.valid) { - return NextResponse.json({ error: "Bad Request", message: validationResult.message }, { status: 400 }); - } - - try { - const created = await store.create(validationResult.data as CreateOfferingInput); - return NextResponse.json(created, { status: 201 }); - } catch (error) { - return NextResponse.json({ error: "Service Unavailable", message: "Storage failure" }, { status: 503 }); - } -} - -function validateInput(body: any): { valid: boolean; message?: string; data?: Partial } { - if (typeof body !== "object" || body === null) { - return { valid: false, message: "Request body must be an object" }; - } - - const allowedKeys = ["name", "description", "price", "category", "specialOffer"]; - const keys = Object.keys(body); - - for (const key of keys) { - if (!allowedKeys.includes(key)) { - return { valid: false, message: `Unknown or identity field: ${key}` }; - } - } - - if (typeof body.name !== "string" || body.name.trim().length === 0 || body.name.trim().length > 30) { - return { valid: false, message: "Name must be a non-empty string with max length 30" }; - } - - if (body.description !== undefined && (typeof body.description !== "string" || body.description.length > 200)) { - return { valid: false, message: "Description must be a string with max length 200" }; - } - - if (typeof body.price !== "number" || body.price < 0) { - return { valid: false, message: "Price must be a non-negative number" }; - } - if (!/^\d+(\.\d{1,2})?$/.test(body.price.toString())) { - return { valid: false, message: "Price must have at most two decimal places" }; - } - - const allowedCategories = ["Coffee", "Tea", "Smoothie"]; - if (typeof body.category !== "string" || !allowedCategories.includes(body.category)) { - return { valid: false, message: "Invalid category" }; - } - - if (body.specialOffer !== undefined && typeof body.specialOffer !== "boolean") { - return { valid: false, message: "Special offer must be a boolean" }; - } - - return { valid: true, data: body as CreateOfferingInput }; + return postHandler(publicStore, request); } diff --git a/src/lib/offering-store.ts b/src/lib/offering-store.ts index 2dd107e..274fc89 100644 --- a/src/lib/offering-store.ts +++ b/src/lib/offering-store.ts @@ -11,11 +11,7 @@ export class InMemoryOfferingStore implements OfferingStore { private nextId = OfferingList.length + 1; async list(): Promise { - return [...this.offerings].sort((a, b) => { - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - return a.ID.localeCompare(b.ID); - }); + return [...this.offerings]; } async create(input: CreateOfferingInput): Promise { @@ -31,3 +27,14 @@ export class InMemoryOfferingStore implements OfferingStore { return newOffering; } } + +export class SortedTestOfferingStore extends InMemoryOfferingStore { + async list(): Promise { + const data = await super.list(); + return [...data].sort((a, b) => { + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return a.ID.localeCompare(b.ID); + }); + } +} diff --git a/tests/offerings-handlers.test.mjs b/tests/offerings-handlers.test.mjs new file mode 100644 index 0000000..bfbf2ff --- /dev/null +++ b/tests/offerings-handlers.test.mjs @@ -0,0 +1,60 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { getHandler, postHandler } from "../src/app/api/offerings/handlers.ts"; +import { SortedTestOfferingStore } from "../src/lib/offering-store.ts"; + +const testStore = new SortedTestOfferingStore(); + +function createMockRequest(body, contentType = "application/json") { + return new Request("http://localhost/api/offerings", { + method: "POST", + headers: { "Content-Type": contentType }, + body: JSON.stringify(body), + }); +} + +test("GET returns sorted offerings from test store", async () => { + const res = await getHandler(testStore); + const data = await res.json(); + assert.equal(res.status, 200); + assert.equal(data[0].name, "Cafe au Lait"); + assert.equal(data[1].name, "Cappuccino"); +}); + +test("POST creates an offering in test store", async () => { + const input = { + name: "Test Coffee", + description: "A lovely test coffee", + price: 4.99, + category: "Coffee", + specialOffer: true, + }; + const req = createMockRequest(input); + const res = await postHandler(testStore, req); + const data = await res.json(); + assert.equal(res.status, 201); + assert.equal(data.name, "Test Coffee"); + assert.equal(typeof data.ID, "string"); +}); + +test("POST rejects unknown fields", async () => { + const req = createMockRequest({ name: "Bad", price: 1, category: "Coffee", hacker: "bad" }); + const res = await postHandler(testStore, req); + assert.equal(res.status, 400); +}); + +test("POST rejects malformed JSON", async () => { + const req = new Request("http://localhost", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "{ bad json }", + }); + const res = await postHandler(testStore, req); + assert.equal(res.status, 400); +}); + +test("POST rejects wrong content type", async () => { + const req = createMockRequest({ name: "A", price: 1, category: "Coffee" }, "text/plain"); + const res = await postHandler(testStore, req); + assert.equal(res.status, 415); +}); From a04ef705d346535fbe41f74536fe43d409198156 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Mon, 12 Oct 2026 09:57:07 -0700 Subject: [PATCH 28/29] feat: integrate milestone 3 offerings foundation (#31) * feat: implement milestone 3 release foundation (#30) * fix: harden API feedback and finalize release evidence * docs: prepare milestone 3 foundation merge and handoff --- .env.local.example | 8 + .eslintrc.json | 8 - .github/workflows/ci.yml | 13 +- .gitignore | 1 + .nvmrc | 1 + AGENTS.md | 10 + README.md | 93 +- docs/adr/003-public-offering-management.md | 11 + docs/contributing.md | 2 +- docs/evidence/milestone-3-menu-desktop.jpg | Bin 0 -> 126073 bytes docs/evidence/milestone-3-menu-mobile.jpg | Bin 0 -> 102323 bytes docs/evidence/milestone-3-preview-error.jpg | Bin 0 -> 22923 bytes docs/evidence/milestone-3-specials-mobile.jpg | Bin 0 -> 37909 bytes docs/getting-started.md | 16 +- docs/milestone-3-deployment.md | 44 + docs/milestone-3-evidence.md | 71 +- docs/milestone-3-plan.md | 70 + docs/milestone-3-review.md | 33 + docs/offering-api-contract.md | 10 + docs/offering-storage.md | 2 +- docs/project-setup.md | 17 +- eslint.config.mjs | 13 + package-lock.json | 11403 +++++++++------- package.json | 45 +- scripts/live-acceptance.ts | 133 + scripts/verify-live.ts | 30 + src/app/api/offerings/[id]/route.ts | 14 + src/app/api/offerings/route.ts | 12 +- src/app/favicon.ico | Bin 39535 -> 0 bytes src/app/globals.css | 24 + src/app/icon.svg | 11 + src/app/manage-offerings/page.tsx | 199 +- src/components/ManageOfferings.tsx | 311 + src/components/OfferingForm.tsx | 4 +- src/components/OfferingsPage.tsx | 25 +- src/components/SpecialOfferingControl.tsx | 11 +- src/database/db.ts | 8 +- src/database/offeringStore.ts | 16 +- src/lib/offering-response.ts | 38 + src/lib/offerings.ts | 41 +- src/proxy.ts | 8 + src/server/management-access.ts | 50 + src/server/offering-handlers.ts | 90 + src/styles/manage-offerings.module.css | 17 + src/styles/offerings.module.css | 29 + src/types/offering.ts | 9 +- tests/helpers/http-server.mjs | 116 +- tests/offering-form.test.mjs | 6 +- tests/offerings-http.test.mjs | 107 +- tests/seed-offerings.test.mjs | 75 + tests/unit/README.md | 18 +- tests/unit/customer-pages.test.tsx | 60 +- tests/unit/live-acceptance.test.ts | 43 + tests/unit/management.test.tsx | 128 + tests/unit/offering-handlers.test.ts | 128 + tsconfig.json | 14 +- vercel.json | 6 + vitest.config.mts | 2 +- 58 files changed, 8000 insertions(+), 5654 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 .nvmrc create mode 100644 docs/adr/003-public-offering-management.md create mode 100644 docs/evidence/milestone-3-menu-desktop.jpg create mode 100644 docs/evidence/milestone-3-menu-mobile.jpg create mode 100644 docs/evidence/milestone-3-preview-error.jpg create mode 100644 docs/evidence/milestone-3-specials-mobile.jpg create mode 100644 docs/milestone-3-deployment.md create mode 100644 docs/milestone-3-plan.md create mode 100644 docs/milestone-3-review.md create mode 100644 eslint.config.mjs create mode 100644 scripts/live-acceptance.ts create mode 100644 scripts/verify-live.ts create mode 100644 src/app/api/offerings/[id]/route.ts delete mode 100644 src/app/favicon.ico create mode 100644 src/app/icon.svg create mode 100644 src/components/ManageOfferings.tsx create mode 100644 src/lib/offering-response.ts create mode 100644 src/proxy.ts create mode 100644 src/server/management-access.ts create mode 100644 src/server/offering-handlers.ts create mode 100644 tests/seed-offerings.test.mjs create mode 100644 tests/unit/live-acceptance.test.ts create mode 100644 tests/unit/management.test.tsx create mode 100644 tests/unit/offering-handlers.test.ts create mode 100644 vercel.json diff --git a/.env.local.example b/.env.local.example index b4181b9..68fb8fe 100644 --- a/.env.local.example +++ b/.env.local.example @@ -2,3 +2,11 @@ # Use the Atlas-generated URI with your explicitly assigned developer database. # Dashboard login and database credentials are separate. Never commit credentials. MONGO_URI={mongo-uri-with-explicit-developer-database-here} + +# Private team management access. Use a unique password of at least 16 characters. +# These variables are server-only. Never use a NEXT_PUBLIC_ prefix. +MANAGEMENT_USERNAME=team +MANAGEMENT_PASSWORD={replace-with-a-long-random-password} + +# Optional deliberate live smoke test. Use HTTPS remotely, loopback HTTP locally. +# LIVE_BASE_URL=https://your-verified-deployment.vercel.app diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index eac53f1..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "extends": ["next/core-web-vitals", "prettier"], - "plugins": ["prettier"], - "rules": { - "prettier/prettier": ["error", { "endOfLine": "auto" }] - } -} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cc7d0b..b0abbec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: # Use matrix strategy to test multiple Node.js versions strategy: matrix: - node-version: [18.x, 20.x, 22.x] + node-version: [24.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ # Steps represent a sequence of tasks that will be executed as part of the job @@ -42,11 +42,12 @@ jobs: - name: Check types run: npm run typecheck - - name: Dry-run build + - name: Run tests + run: npm test + + - name: Build without Atlas credentials run: npm run build env: - # Routine checks use controlled responses or disposable local MongoDB. MONGO_URI: "" - - - name: Run tests - run: npm test + MANAGEMENT_USERNAME: "" + MANAGEMENT_PASSWORD: "" diff --git a/.gitignore b/.gitignore index 1cef719..1e8fa72 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # testing /coverage +/.vitest/ # next.js /.next/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/AGENTS.md b/AGENTS.md index 2863c5d..bb82d59 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,3 +30,13 @@ Reviewers are learning TypeScript, React, Next.js, and Git. Several are coming f - **Issue tracker** — `docs/agents/issue-tracker.md` when you publish a spec, fetch a ticket, or edit an issue. The tracker is GitHub Issues for `BrianLiDesign/coffee-shop`. - **Triage labels** — `docs/agents/triage-labels.md` when a skill names a triage role. Apply this repo's label for that role. - **Domain language** — `docs/agents/domain.md` when you name a shop concept in an issue, test, or refactor, or when a change would contradict a recorded decision. Glossary: root `CONTEXT.md`. Decisions: `docs/adr/`. + + + +## This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/README.md b/README.md index 3aa284c..542fb70 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,74 @@ # Coffee Shop -A student-built shop site. Customers will browse drinks and food, customize a drink, read the shop story, see discounts, and choose pickup or delivery. The team is learning TypeScript, React, Next.js, and Git while building it. +A student-built coffee shop site. Customers browse stored offerings and specials, read the shop story, and find contact information. Authorized team members can create, edit, and delete offerings through the management page. -Coding agents that change this repo follow [AGENTS.md](AGENTS.md). +**Existing public site:** [Coffee Shop on Vercel](https://coffee-shop-sigma-rouge.vercel.app/). The Milestone 3 foundation is integrated through PR #31; production deployment, database configuration, and live operation verification must be verified separately. The [Milestone 3 preview](https://coffee-shop-git-feat-milestone-3-release-brianlidesign.vercel.app/) has Vercel sign-in protection. See the [deployment guide](docs/milestone-3-deployment.md) and [release evidence](docs/milestone-3-evidence.md). A successful local build is not live deployment evidence. -## What works today +## Features -The home page at `/` renders without a database. About, Contact, Menu, and Specials are available. Menu and Specials read the 16 sample offerings from `GET /api/offerings`; three are marked as special. Offering IDs are strings, and `npm test` runs real HTTP GET checks. A starter page lives at `/example`, and `GET /api/example` connects to MongoDB. +- Public pages: Home (`/`), About (`/about`), Menu (`/menu`), Specials (`/specials`), and Contact (`/contact`). +- Menu and Specials read MongoDB through `/api/offerings`; Specials uses the saved boolean flag. +- Protected management at `/manage-offerings`: creation, editing, delete confirmation, validation, and pending/success/error feedback. +- Loading, empty, error, and retry states, with responsive layouts. +- Repeatable insert-only seeding of the 16 starting offerings, including three specials. +- Vitest unit/component tests plus real HTTP and disposable database regression tests. -Database-backed offerings, creation, and the management page are planned Milestone 2 work. +Create/read are the required Milestone 3 operations. Update/delete are Brian's additional assignment. Customer orders, payments, drink customization, and delivery integrations remain future work. Starter examples at `/example` and `/api/example` are learning references. -## What we are building +## Tech stack -- Browse drinks with ingredients and prices -- Customize a drink before ordering -- Browse food and seasonal offerings -- Read the coffee shop story -- Choose pickup or delivery through Grubhub or DoorDash -- See available discounts +Next.js 16 App Router, React 19, TypeScript, CSS Modules, MongoDB Atlas, Mongoose 8, Vitest 5, React Testing Library, ESLint, Prettier, and Vercel. Use **Node.js 24.x** and npm, matching CI and the deployment runtime. -## Team +## Local setup -- Brian — project tech lead -- Jean — backend -- Naomi — database -- Vedika — frontend -- Aditi — frontend -- Sofie — backend +1. Clone the repository and open its root directory. +2. Install Node.js 24.x, then run `npm ci`. +3. Copy `.env.local.example` to the ignored `.env.local`. +4. Privately replace the MongoDB placeholder with a URI containing your assigned database name. Set your management username and a unique password of at least 16 characters. +5. Run `npm run db:check` to verify real read access. +6. To deliberately seed this database, run `npm run db:seed -- --apply`. Reruns insert missing seed records and preserve existing records and edits. +7. Run `npm run dev` and open [localhost:3000](http://localhost:3000). +8. Open [management](http://localhost:3000/manage-offerings) and sign in through the browser's credential prompt. -## Start here +The home page runs without MongoDB. Database pages show a readable error when configuration or connectivity is unavailable; there is no sample-data fallback. Do not share populated environment files or screenshots of credentials. -Learning developers start with the first two guides. Coding agents start with `AGENTS.md`, then open a guide when the task matches it. +## Environment variables -1. [Getting started](docs/getting-started.md) — install the app, learn the folders, and run it locally. -2. [Contributing](docs/contributing.md) — one branch and pull request per issue. -3. [Project setup](docs/project-setup.md) — tools already configured in this repository. -4. [AGENTS.md](AGENTS.md) — how a coding agent should change this repo. +| Variable | Purpose | +| --------------------- | --------------------------------------------------------------------------------------------- | +| `MONGO_URI` | Server-side MongoDB connection string with an explicit shop database | +| `MANAGEMENT_USERNAME` | Private team management username; must not contain a colon | +| `MANAGEMENT_PASSWORD` | Private management password, at least 16 characters; placeholder values are rejected | +| `LIVE_BASE_URL` | Optional HTTPS origin for deliberate live acceptance checks; loopback HTTP is allowed locally | -## Customer-page testing: start here +Configure these privately in Vercel for the relevant environment. Never hardcode credentials or use a `NEXT_PUBLIC_` prefix for them. Keep preview and production database targets separate. -The [Vitest handoff](tests/unit/README.md) explains the runnable customer-page -starter on `main`, Vedika's three unimplemented cases for #35, and verification -commands. Record personal contributions in the -[Milestone 3 evidence table](docs/milestone-3-evidence.md). Customer-page testing -can start before PR #31 merges; release-only tests remain on that branch. +## Tests and quality checks -## Milestone 2: start your assignment +- `npm run test:unit` runs Vitest once; `npm run test:watch` runs it interactively. +- `npm run test:regression` checks real HTTP persistence, seeding, database access tooling, and existing frontend behavior. +- `npm test` runs both suites. +- `npm run preflight` runs lint, typecheck, all tests, and a production build. +- `npm run db:check:write` deliberately verifies write/read/cleanup using its own disposable marker. +- `npm run test:live` reads the explicitly configured live target without writing. +- `npm run test:live -- --confirm-writes` verifies live create/read and cleans up only its disposable offering. +- `npm run test:live -- --confirm-writes --all` also verifies updates. Cleanup verifies deletion. -Open the [independent developer plan](docs/milestone-2-parallel-work.md), find your issue, and read its acceptance criteria. Every assignment can be built, tested, reviewed, and merged without waiting for another developer's issue to finish. +Automated persistence checks start disposable loopback MongoDB instances and require no Atlas credentials. The first run may download MongoDB 7.0.24. Each developer must personally contribute and explain at least three relevant Vitest cases; the shared suite does not establish individual completion. -- [API contract](docs/offering-api-contract.md): the shared input, response, and error agreement. -- [Developer guide](docs/milestone-2-developer-guide.md): Atlas setup and individual verification. Run `npm run db:check` for a real read or explicitly run `npm run db:check:write` for disposable write/read/cleanup. Seed tooling is separate work. -- [Milestone spec](docs/milestone-2-spec.md): the final integrated requirements. -- [Glossary](CONTEXT.md): offering, special offering, menu, and offering management. +## Deployment -Independent assignment completion and full milestone completion are different. Brian coordinates final integration and each developer's own Atlas evidence under [#12](https://github.com/BrianLiDesign/coffee-shop/issues/12). +Follow the [Vercel release guide](docs/milestone-3-deployment.md). Keep customer pages public and management protected. Verify live persistence, responsive behavior, and failure states before replacing the pending live URL above. + +## Team and workflow + +After PR #31 merges, developers can start from updated `main`, including Sofie's +acceptance safeguards in issue #36. Vedika's customer-page test foundation was +already available before that merge. The [test handoff](tests/unit/README.md) +identifies her starter file and three assigned Vitest cases. Preserve submitted +contributions and record personal evidence in +[`docs/milestone-3-evidence.md`](docs/milestone-3-evidence.md). + +Brian: tech lead, integration, access control, deployment, documentation, update/delete. Jean: backend handlers. Naomi: database and seeding. Vedika: customer frontend. Aditi: management frontend. Sofie: acceptance tooling and release evidence. + +See the [Milestone 3 plan](docs/milestone-3-plan.md) for assignments, acceptance criteria, and the release checklist. Follow [Contributing](docs/contributing.md): one issue, one branch, one reviewed PR into main. Learning developers can start with [Getting started](docs/getting-started.md); agents follow [AGENTS.md](AGENTS.md). diff --git a/docs/adr/003-public-offering-management.md b/docs/adr/003-public-offering-management.md new file mode 100644 index 0000000..03082d2 --- /dev/null +++ b/docs/adr/003-public-offering-management.md @@ -0,0 +1,11 @@ +# Public offering management for Milestone 3 + +Status: accepted for the implementation branch on October 7, 2026. + +Brian approved implementing the Milestone 3 plan, including controlled management access and additional update/delete operations. This supersedes Milestone 2's local-only write restriction for this release. + +Customer pages and GET offerings are public. Management uses private HTTP Basic team credentials over HTTPS in deployment. Proxy provides the browser challenge, while the server page and each write handler independently enforce access. Missing or placeholder configuration denies access. Browser writes from another origin are rejected. Secrets are read only on the server. + +This student release intentionally has no individual accounts, roles, session database, or password recovery. Browsers cache Basic credentials; ending the browsing session is the practical sign-out mechanism. Individual staff access is a separate future design. + +Brian also approved a supported framework upgrade after the production dependency audit found critical vulnerabilities in Next.js 14. The release uses Next.js 16, React 19, Node.js 24, ESLint flat configuration, asynchronous request APIs, and `proxy.ts`. The plan and deployment guide record verification and release boundaries. diff --git a/docs/contributing.md b/docs/contributing.md index 288cddf..d0eb8d5 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -9,7 +9,7 @@ Every change lands on its own branch and pull request. Do not commit directly to ```bash git checkout main git pull origin main - npm install + npm ci ``` 2. Open the GitHub issue assigned to you. Use **Create a branch for this issue** when GitHub offers it. Otherwise create the branch locally: diff --git a/docs/evidence/milestone-3-menu-desktop.jpg b/docs/evidence/milestone-3-menu-desktop.jpg new file mode 100644 index 0000000000000000000000000000000000000000..83cc5e2e5eab1b09c69e8d3bf61f5bd12bf96882 GIT binary patch literal 126073 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 JEB$@^KLEl{R**=w!6ch1>4=d;&mt;=7RbAVe< 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 V|5fzz|Hu7bFs%Q5row$S`ETYu;nx5F literal 0 HcmV?d00001 diff --git a/docs/evidence/milestone-3-preview-error.jpg b/docs/evidence/milestone-3-preview-error.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ead5bde3e00738033878be87d66a29f45a28c26b GIT binary patch literal 22923 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 F{{`GqR0{wA literal 0 HcmV?d00001 diff --git a/docs/evidence/milestone-3-specials-mobile.jpg b/docs/evidence/milestone-3-specials-mobile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e50b79bbeac0f1a28c45ecbb19d030b0ec0e9d9c GIT binary patch literal 37909 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 Mir*k)+2;Q@0jxwit^fc4 literal 0 HcmV?d00001 diff --git a/docs/getting-started.md b/docs/getting-started.md index 15af633..d79b321 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -13,10 +13,10 @@ Install the app, learn the languages and folders, and run it on your machine. ## Setup for all developers -1. Install [Node.js](https://nodejs.org/) 18 or newer. Continuous integration runs on Node 18, 20, and 22. +1. Install [Node.js](https://nodejs.org/) 24.x, matching continuous integration and Vercel. 2. Clone this repository: `git clone ` -3. From the repository root, run `npm install`. -4. Copy `.env.local.example` to `.env.local`. Ask the tech lead for the `MONGO_URI` value and paste it into that file. Next.js reads `.env.local` automatically, and git ignores it. +3. From the repository root, run `npm ci`. +4. Copy `.env.local.example` to `.env.local`. Configure your private `MONGO_URI` and management credentials following README. Next.js reads `.env.local` automatically, and git ignores it. 5. Open the folder in VS Code or Cursor and install the recommended extensions when prompted: ESLint and Prettier. `.vscode/settings.json` already turns on format on save. ESLint runs Prettier, so leave the default formatter as the workspace setting. 6. Read [Languages](#languages), [Project structure](#project-structure), and [Contributing](./contributing.md) before you change code. @@ -27,9 +27,9 @@ The home page at `/` loads without MongoDB. `GET /api/example` calls the databas | Language or tool | Where it shows up | What you use it for | | -------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------ | | TypeScript (`.ts`, `.tsx`) | `src/` | Pages, components, API routes, and database code. `tsconfig.json` enables strict checking. | -| React 18 | `src/app` and `src/components` | The user interface. Components are functions that return JSX. | +| React 19 | `src/app` and `src/components` | The user interface. Components are functions that return JSX. | | CSS | `src/app/globals.css` and `src/styles/*.module.css` | Global rules, plus CSS Modules for a single component. | -| Next.js 14 | `src/app` | App Router pages and API route handlers. | +| Next.js 16 | `src/app` | App Router pages and API route handlers. | | JavaScript | `next.config.js`, `prettier.config.js` | Tool configuration. Application features go in TypeScript under `src/`. | | Mongoose | `src/database` | Talks to MongoDB. | @@ -44,7 +44,7 @@ coffee-shop/ │ │ ├── layout.tsx Shared shell. Renders Navbar on every page. │ │ ├── page.tsx Home page, route / │ │ ├── globals.css Global CSS -│ │ ├── favicon.ico +│ │ ├── icon.svg Coffee shop icon │ │ ├── example/page.tsx Example page, route /example │ │ └── api/example/route.ts Example API, GET /api/example │ ├── components/ Shared React components (Navbar.tsx) @@ -60,7 +60,7 @@ coffee-shop/ `skills/` and `docs/agents/` are instructions for coding agents. Feature work belongs in `src/`. -There is no `public/` folder. The favicon lives at `src/app/favicon.ico`, which is the App Router location. +There is no `public/` folder. The icon lives at `src/app/icon.svg`, which is an App Router metadata location. ## How a page is wired @@ -68,7 +68,7 @@ A `page.tsx` file becomes a URL. `src/app/page.tsx` is `/`. A folder under `src/ `src/app/layout.tsx` wraps every page and renders `Navbar` once. Leave the navbar import out of individual pages. -The navbar links to `/`, `/about`, `/menu`, `/specials`, and `/contact`. Only `/` and `/example` have page files today. The other routes appear in the navbar so later page work can attach to them. +The navbar links to the implemented customer pages `/`, `/about`, `/menu`, `/specials`, and `/contact`. Authorized team members use `/manage-offerings`. An API route is a `route.ts` file under `src/app/api`. `src/app/api/example/route.ts` handles `GET /api/example` and calls `connectDB()` from `src/database/db.ts`. diff --git a/docs/milestone-3-deployment.md b/docs/milestone-3-deployment.md new file mode 100644 index 0000000..876e7fd --- /dev/null +++ b/docs/milestone-3-deployment.md @@ -0,0 +1,44 @@ +# Milestone 3: Vercel deployment + +This release uses Next.js 16, React 19, and Node.js 24.x. It implements real offering persistence and protected team management. The [plan](milestone-3-plan.md) is tracked under [#30](https://github.com/BrianLiDesign/coffee-shop/issues/30). + +## Prepare the release + +1. Run `npm ci` and `npm run preflight`. Resolve failures before deploying. +2. Review the branch and select the exact commit to deploy. Main is the eventual production branch; a feature-branch deployment is preview evidence until production is separately verified. +3. Prepare a named Atlas deployment database and a database user with scoped read/write access. Confirm the network access policy supports the deployment's egress. Do not copy a developer's unrestricted credential into Vercel. +4. Prepare a different database and credentials for Preview. Ordinary pull request checks must never write into production data. + +## Configure Vercel + +1. Sign in to the intended Vercel account/team and import `BrianLiDesign/coffee-shop`, or select its existing project. +2. Use the repository root, Next.js framework preset, Node.js 24.x, `npm ci`, and `npm run build`. `vercel.json` and `package.json` record the build/runtime agreement. +3. In project environment settings, privately configure `MONGO_URI`, `MANAGEMENT_USERNAME`, and `MANAGEMENT_PASSWORD` for Production and separately for Preview. Choose a unique management password of at least 16 characters; placeholder values are denied. +4. Keep secret names server-only. Never add a `NEXT_PUBLIC_` prefix or commit populated environment files. Save credentials privately, not in issues, PRs, logs, or screenshots. +5. Deploy a new build after changing environment variables. Existing deployments do not acquire changed values automatically. +6. If the preview has Vercel deployment protection, use its authorized browser access to inspect it. Do not disable protection merely to make an acceptance script pass. + +Vercel references: [environment variables](https://vercel.com/docs/environment-variables), [deployment environments](https://vercel.com/docs/deployments/environments). + +## Seed deliberately + +Seeding never happens automatically during builds or requests. In a private local shell configured with the intended deployment database URI, run `npm run db:seed -- --apply`. Confirm the target privately before running it. Seed reruns use stable internal keys and insert-only behavior, so they preserve existing edits and unrelated records. Removing a seeded offering and deliberately rerunning the seed recreates that missing seed record. + +## Verify the live application + +1. Open the public root, About, Contact, Menu, and Specials URLs without team credentials. Confirm database-backed offerings load and the pages work on desktop and narrow mobile screens. +2. Open `/manage-offerings`. The browser requests team credentials. All writes are separately checked inside the route handlers; the server-rendered management page also checks access independently of proxy. +3. Configure `LIVE_BASE_URL` privately with the exact HTTPS origin. Run `npm run test:live` for a read-only check. +4. For deliberate live create/read evidence, configure the matching management credentials and run `npm run test:live -- --confirm-writes`. The runner creates a uniquely marked offering, verifies HTTP read-back, deletes only its own marked record, and confirms cleanup. +5. Run `npm run test:live -- --confirm-writes --all` to verify Brian's additional editing behavior too. Never treat a local disposable-database result as live evidence. +6. In the browser, create a normal offering and a special, refresh, and confirm Menu/Specials behavior. Record the evidence without credentials. Use clearly disposable records and remove only those records after the demonstration. +7. Demonstrate loading, empty, failed fetch/save, and save-success/reload-failure states in controlled local previews or an isolated preview database. Avoid deliberately breaking the production database for screenshots. +8. Record the production URL, deployed commit, checks, screenshots, and per-developer contributions in [release evidence](milestone-3-evidence.md). Update README's pending URL only after it is verified. + +The smoke runner refuses remote plain HTTP, redirects, credential-bearing URLs, and unknown arguments. Failures print sanitized diagnostics. If cleanup cannot be verified, inspect the uniquely marked Verification offering before retrying; never clear the collection. + +## Team access limitations + +HTTP Basic over HTTPS is a small team-access mechanism for this student release, not an individual account or audit system. Use private credentials and rotate them when team access changes. Browsers may cache credentials until the browsing session closes. No password is stored in frontend state, source, or local storage. Cross-origin browser writes are denied. Production with individual staff accounts would need a separate authentication design. + +If deployment fails, check Vercel's build/runtime diagnostics privately, the selected environment, Atlas permissions/network access, and the explicit database name. Do not paste connection strings or raw database errors into a PR. diff --git a/docs/milestone-3-evidence.md b/docs/milestone-3-evidence.md index f657209..a96e0ee 100644 --- a/docs/milestone-3-evidence.md +++ b/docs/milestone-3-evidence.md @@ -1,28 +1,57 @@ # Milestone 3 release evidence -## Test foundation +## Verification boundary -Current `main` includes Vitest configuration, controlled component-test helpers, -and one loading example in `tests/unit/customer-pages.test.tsx`. That example is -agent-written foundation coverage, not a developer's personal contribution. -Vedika's three cases for #35 remain for her to write. See the -[test handoff](../tests/unit/README.md). +PR #31 integrates the release foundation so developers can extend it from `main`. Its merge does not complete the public production release, live database proof, or each developer's own contributions; those remain pending until recorded below. Issue #30 stays open to track the remaining release requirements. Agent-written reference tests do not count as evidence that another developer completed their assignment. -Release features and their dependent test suites remain in PR #31 until integrated. -Controlled responses and disposable local databases do not prove live deployment -or Atlas persistence. Keep unverified release results pending. +| Requirement | Evidence to record | Status | +| --------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------- | +| Public Vercel release | Verified production URL and deployed Git commit | Existing project confirmed; this branch pending deployment | +| Live create/read persistence | Live acceptance output; browser refresh and Atlas inspection of the same offering ID | Pending deployment | +| Additional update/delete | Live all-operations acceptance and targeted cleanup | Pending deployment | +| README | Description, setup, tech stack, tests, accurate verified live link | Implemented; existing live link recorded | +| Loading, empty, responsive states | Controlled state checks and desktop/mobile screenshots | Automated coverage and local desktop/mobile evidence recorded | +| Frontend API errors | Failed fetch/save and successful save with failed reload | Automated coverage implemented | +| Vitest contribution per developer | At least three personally authored cases and reviewed PR per person | Pending individual submissions | ## Developer evidence -| Developer | PR | Three personally authored test names | What each test protects | Browser or other evidence | Explained and reviewed | -| --------- | ------- | ------------------------------------ | ----------------------- | ------------------------- | ---------------------- | -| Brian | Pending | Pending | Pending | Pending | Pending | -| Jean | Pending | Pending | Pending | Pending | Pending | -| Naomi | Pending | Pending | Pending | Pending | Pending | -| Vedika | Pending | Pending | Pending | Pending | Pending | -| Aditi | Pending | Pending | Pending | Pending | Pending | -| Sofie | Pending | Pending | Pending | Pending | Pending | - -Record browser evidence with route, viewport, commit, result, and screenshot link. -Keep credentials and populated environment files out of evidence. Carry this -contribution record forward when PR #31 adds its release verification matrix. +The foundation on `main` includes one agent-written loading example and controlled +test helpers. Vedika's three assigned Vitest cases remain for her to write; she +does not need to wait for PR #31. See the [test handoff](../tests/unit/README.md). +Preserve this contribution record and submitted cases during release integration. +For each contributed test, explain what it protects. Record browser evidence with +route, viewport, commit, result, and screenshot link. + +| Developer | PR | Three test case names | Explained and reviewed | +| --------- | ------- | --------------------- | ---------------------- | +| Brian | Pending | Pending | Pending | +| Jean | Pending | Pending | Pending | +| Naomi | Pending | Pending | Pending | +| Vedika | Pending | Pending | Pending | +| Aditi | Pending | Pending | Pending | +| Sofie | Pending | Pending | Pending | + +## Local release checks + +Record the command, result, and commit after the final branch checks. Local disposable MongoDB and controlled HTTP responses verify application behavior; they do not establish the deployment database's connectivity. + +- Lint: passed. +- Typecheck: passed. +- Vitest: 31 passed, including same-origin and malformed error-envelope regressions. +- HTTP/database/UI regression suite: 51 passed after reconciling main; includes retained storage/seed checks, restart persistence, and live-runner cleanup. +- Production build: passed; routes do not query Atlas during build. +- Production dependency audit: zero findings after the approved Next.js/React upgrade and Mongoose patch update; rerun before deployment. +- Standards/spec review: no blocking defects; documented follow-ups resolved. See [review report](./milestone-3-review.md). + +Foundation merge check on October 12, 2026: `npm ci` and `npm run preflight` +passed on Node 24.19.0 against implementation `3d90071` with the handoff/documentation +corrections in this PR. All 31 Vitest and 51 Node regression cases passed, followed +by the production build. `npm audit --omit=dev` reported zero vulnerabilities. +These checks used disposable databases and do not establish live Atlas evidence. + +Local browser evidence: seeded Menu at desktop and 390px mobile widths, no horizontal overflow; created a special through management, edited it, and verified it on Specials after navigation. These checks used disposable local MongoDB. Browser evidence is in `docs/evidence/milestone-3-*.jpg`. + +At the original preview inspection, the Vercel project environment panel showed no project variables. Preview deployed commit `693cc0c` successfully at https://coffee-shop-git-feat-milestone-3-release-brianlidesign.vercel.app/. Vercel Authentication protected it from anonymous requests (302). The authenticated browser showed a clear API-unavailable alert and retry button (`docs/evidence/milestone-3-preview-error.jpg`). These observations are historical. Merging PR #31 can trigger a production deployment; a passing deployment check does not verify private environment configuration or live create/read persistence. Record fresh production acceptance separately. + +Do not record credentials, populated environment files, or raw database exceptions in evidence. diff --git a/docs/milestone-3-plan.md b/docs/milestone-3-plan.md new file mode 100644 index 0000000..22eaae2 --- /dev/null +++ b/docs/milestone-3-plan.md @@ -0,0 +1,70 @@ +# Milestone 3: live offerings and release quality + +## Approved scope + +Brian requested a new branch implementing the release features and this plan. Create/read are the required Milestone 2 operations. Update/delete are Brian's additional assignment. Brian subsequently approved a supported Next.js upgrade after the deployment audit found critical vulnerabilities in the old runtime. Customer ordering, payments, and a redesign remain outside this release. + +The implementation branch is `feat/milestone-3-release`, based on main at `4db8d6f`. It integrates the outstanding persistence work without merging or closing other developers' existing assignments. This release supersedes Milestone 2's restriction against public deployment of the management interface: customer routes stay public, but management and all writes require server-enforced team credentials. + +## Acceptance criteria + +- GET `/api/offerings` reads MongoDB at request time, returns only public offering fields with string IDs, sorts by name then ID, and never falls back to sample data. +- Authorized POST creates a validated offering. Refresh reads the same persisted ID. Special offerings appear on Specials. +- Brian's additional PUT `/api/offerings/[id]` replaces offering details and DELETE removes the selected offering. Missing records return 404. Invalid IDs/input return 400. +- `/manage-offerings` requires HTTP Basic team credentials over HTTPS in deployment. Every write handler independently checks credentials and rejects cross-origin browser writes. Missing access configuration denies access. Secrets are server-only environment variables; there is no client-side credential storage. +- Management supports create, edit, cancel, explicit delete confirmation, pending locks, retained input on failure, and accurate save-success/reload-failure feedback. +- Menu, Specials, and management show loading, empty, readable error, and retry states and work on narrow screens and with a keyboard. +- Vitest unit/component checks run alongside existing regression checks in CI. Ordinary checks use disposable local MongoDB, never shared Atlas credentials. +- Repeatable insert-only seeding adds the 16 starting offerings and preserves edits and unrelated records on rerun. +- A live acceptance command checks authorized create/read (and optionally update/delete) against an explicit target using uniquely marked test records and cleans up only records it created. +- README explains the actual app, tech stack, local setup, environment variables, tests, deployment, and the verified public URL. Until deployed, the live link is explicitly pending. +- Release evidence records the deployment URL and commit, live persistence, desktop/mobile screenshots, failure/empty/loading checks, and each developer's own three Vitest contributions. Agent-written tests do not establish personal developer completion. + +## Brian's work order + +1. Establish Vitest, a supported Node runtime, and CI quality gates. +2. Implement and connect validated database-backed handlers and insert-only seeding. +3. Protect management and all writes, then complete create/edit/delete UI composition. +4. Verify real HTTP persistence against disposable MongoDB and controlled frontend states. +5. Configure Vercel Production and Preview with separate database targets, deploy, and run live acceptance. +6. Finalize README and collect submission evidence. Review and commit the branch; merge requires separate authorization. + +## Developer ownership and personal evidence + +These are follow-up contribution assignments, not claims that a developer authored this branch's tests. Each developer owns at least three relevant Vitest cases, explains what failure they prevent, and supplies a reviewed PR. Existing Milestone 2 issues remain independently owned. + +| Developer | Area | Minimum personal Vitest behaviors | +| --------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| Naomi | Storage and seeding | Public serialization excludes metadata; omitted special flag defaults false; seed rerun preserves edits and unrelated records | +| Jean | API validation and errors | Valid creation returns 201; invalid input leaves data unchanged; database failure returns sanitized 503 | +| Aditi | Management form | Pending submission prevents duplicates; failed save preserves fields; confirmed save followed by failed reload reports both outcomes | +| Vedika | Customer pages and responsive checks | No-special-offerings feedback; rejected network request shows an error without empty feedback; unreadable API errors show useful fallback feedback | +| Sofie | Acceptance tooling and evidence | Wrong response status fails acceptance; malformed offering fails; missing read-back fails; maintain browser/live checklist | +| Brian | Integration, access, deployment, documentation, update/delete | Unauthenticated write denied; authorized write works; missing configuration fails closed; add update/delete regressions | + +## Test boundaries + +Use the agreed HTTP request/response boundary and rendered UI interactions. Unit tests may supply controlled external storage/HTTP responses; integration tests exercise real routes backed by a disposable database. Passing controlled tests is not Atlas or live Vercel evidence. Do not count snapshots, constants, or assertions against private implementation as useful integrity cases. + +## Release checklist + +- [ ] Choose submission date and integration checkpoint with the team. +- [ ] Confirm all automated quality gates and code review pass. +- [ ] Configure private `MONGO_URI`, `MANAGEMENT_USERNAME`, and `MANAGEMENT_PASSWORD` in each Vercel environment; use different preview data and credentials. +- [ ] Confirm Atlas network access and scoped read/write permissions for the deployment database. +- [ ] Deliberately seed the selected deployment database, preserving existing records. +- [ ] Public customer pages open without login; unauthorized management/writes are denied. +- [ ] Live create/read and optional update/delete acceptance pass; disposable records are cleaned up. +- [ ] Capture desktop and narrow mobile views, keyboard use, empty/loading/error states, and persistence after refresh. +- [ ] Record verified live URL and deployed commit in README and release evidence. +- [ ] Record three personally contributed Vitest cases and merged PR per developer. + +The existing Vercel project is `brianlidesign/coffee-shop`, with public URL https://coffee-shop-sigma-rouge.vercel.app/. No deadline has been supplied. Deployment, team contributions, and live evidence must remain pending until verified. + +## Foundation integration + +Brian authorized pushing and merging PR #31 into `main` on October 12, 2026 to +unblock follow-up contributions, including Sofie's issue #36. This integrates the +implementation after automated checks and review. It does not complete the +release checklist. Keep parent issue #30 and individual assignments open until +their remaining acceptance criteria have verified evidence. diff --git a/docs/milestone-3-review.md b/docs/milestone-3-review.md new file mode 100644 index 0000000..244cc7a --- /dev/null +++ b/docs/milestone-3-review.md @@ -0,0 +1,33 @@ +# Milestone 3 review + +Reviewed base `4db8d6f` through implementation commit `693cc0c` using parallel Standards and Spec reviewers. Reviewers inspected code and evidence; the primary agent ran checks. + +## Standards + +No blocking documented-standard violations. Routes, aliases, database boundaries, retained starter references, and domain language follow the repository contract. + +One nonblocking heuristic: duplicated error-envelope parsing. Resolved by sharing a parser that accepts only nonempty string messages and known string field errors. Added a rendered management regression proving malformed API fields do not break the form. + +Setup documentation had stale route, test, and GitHub-secret instructions. Corrected the routes and test description and directed deployment secrets to Vercel. + +## Spec + +No blocking code findings or scope creep. Persistent handlers, access, validation, loading/empty/error/retry states, pending locks, confirmation, and save/reload outcomes implement the plan. Update/delete and framework upgrade were explicitly approved. + +External requirements remain partial: public production release, live persistence, and each developer's three personal Vitest contributions. Evidence records these as pending. + +Standards: one nonblocking heuristic plus stale documentation, all resolved. Spec: zero code findings; three external evidence categories remain incomplete. + +## Foundation merge review: October 12, 2026 + +Parallel Standards and Spec reviewers checked `origin/main` at `9054f88` against +PR #31 at `3d90071`, including the retained storage, seed, and test foundation. + +- Standards: zero blocking violations; one nonblocking duplication heuristic for + the accepted offering field names. Existing behavior is covered by tests. +- Spec: zero blocking implementation defects; the test handoff's description of + reference cases and release-only modules was stale. Updated the handoff and + release documentation to reflect foundation integration and Sofie's next step. + +Brian authorized the foundation merge separately from release completion. Keep +issue #30 open for deployment, live persistence, and personal test contributions. diff --git a/docs/offering-api-contract.md b/docs/offering-api-contract.md index 5277513..7cd9342 100644 --- a/docs/offering-api-contract.md +++ b/docs/offering-api-contract.md @@ -1,5 +1,15 @@ # Offering API contract +## Milestone 3 implementation + +The historical Milestone 2 planning text below describes the original assignment boundaries. The Milestone 3 branch now reads MongoDB through GET and accepts authorized POST creation. IDs are MongoDB-generated strings; seed data is inserted deliberately rather than served as a fallback. GET returns sorted public data, `[]` when empty, and a sanitized 503 on database failure. + +Brian's additional PUT `/api/offerings/[id]` replaces all offering details using the same input rules; omitted special flag defaults false. DELETE removes that offering and returns 204. Invalid identities return 400, missing records 404, missing/invalid management access 401, and cross-origin browser writes 403. PUT/DELETE and POST enforce access independently inside each handler. The [Milestone 3 plan](milestone-3-plan.md) and [deployment guide](milestone-3-deployment.md) supersede the old local-only restriction. + +Use Node 24. `npm test` now runs Vitest and the retained Node regression suite. The HTTP harness starts real Next.js routes against a disposable MongoDB database, seeds the starting menu, verifies persisted writes, and checks database failure without a fixture fallback. Ordinary checks never use shared Atlas credentials. + +## Historical Milestone 2 contract and assignment notes + Issue [#14](https://github.com/BrianLiDesign/coffee-shop/issues/14) establishes this contract for [Milestone 2](https://github.com/BrianLiDesign/coffee-shop/issues/12). GET is implemented using fixtures today. In the [independent assignment plan](./milestone-2-parallel-work.md), #15 supplies storage and seeding, #16 supplies independently tested GET/POST handlers and runtime validation, #17 supplies the special control/customer display checks, #20 supplies the management form, and #18 supplies acceptance tooling. Brian composes the real routes and UI during final milestone integration. This guide defines later integrated behavior without claiming it is already implemented. ## Shared types diff --git a/docs/offering-storage.md b/docs/offering-storage.md index 6d4e1d5..0f27963 100644 --- a/docs/offering-storage.md +++ b/docs/offering-storage.md @@ -1,6 +1,6 @@ # Offering storage and repeatable seeding -[Issue #15](https://github.com/BrianLiDesign/coffee-shop/issues/15) supplies real MongoDB storage independently of the live API and customer pages. The existing fixture-backed route remains unchanged. Brian composes the real storage with the independently developed handlers under parent [#12](https://github.com/BrianLiDesign/coffee-shop/issues/12). +[Issue #15](https://github.com/BrianLiDesign/coffee-shop/issues/15) supplied real MongoDB storage independently of the live API and customer pages. Its original deliverable left the fixture-backed route unchanged. The Milestone 3 release now composes that storage with the handlers and adds update/remove operations. The independent storage and seed regressions remain enabled. ## Storage boundary diff --git a/docs/project-setup.md b/docs/project-setup.md index d90660d..d22c461 100644 --- a/docs/project-setup.md +++ b/docs/project-setup.md @@ -2,13 +2,20 @@ These tools are already configured. You do not need to add them before writing a feature. -1. **Next.js 14 App Router.** Pages and route handlers live under `src/app`. See [Getting started](./getting-started.md) for the folder map. +1. **Next.js 16 App Router and React 19.** Pages and route handlers live under `src/app`. See [Getting started](./getting-started.md) for the folder map. Server request APIs and route parameters are asynchronous. 2. **TypeScript in strict mode**, with the `@/*` import alias pointed at `src/`. -3. **React 18** for interface components. +3. **React 19** for interface components. 4. **Mongoose** connection helper in `src/database/db.ts`. It reads `MONGO_URI` and reuses one connection across requests. `src/database/userSchema.ts` is an example schema. 5. **Example routes.** `src/app/example/page.tsx` and `src/app/api/example/route.ts` show a page and an API route. The API route connects to MongoDB. New shop pages should follow the issue you were assigned. -6. **ESLint and Prettier.** ESLint extends `next/core-web-vitals` and reports Prettier issues as errors. Prettier uses a print width of 120. `npm run lint` runs the check. +6. **ESLint and Prettier.** `eslint.config.mjs` imports Next's Core Web Vitals flat configuration and reports Prettier issues as errors. Prettier uses a print width of 120. `npm run lint` runs ESLint directly. 7. **Pre-commit hook.** Husky runs lint-staged, which formats staged files and runs ESLint on JavaScript and TypeScript. 8. **Issue and pull request templates** in `.github`. -9. **Continuous integration.** `.github/workflows/ci.yml` runs on pushes and pull requests to `main` and `develop`. The job installs locked dependencies, checks lint and types, builds without Atlas credentials, and runs both test suites on Node 18, 20, and 22. -10. **Tests.** `npm test` runs Vitest unit/component tests and the existing Node regression suite. `npm run test:unit` selects `tests/unit/**/*.test.{ts,tsx}`; the customer-page starter uses jsdom and controlled fetch responses. Node regressions exercise HTTP, component behavior, database checks, storage, and seeding using isolated tooling. `npm run preflight` runs lint, typecheck, both suites, and the build. See the [test handoff](../tests/unit/README.md) and [Offering contract](./offering-api-contract.md). +9. **Continuous integration.** `.github/workflows/ci.yml` runs on pushes and pull requests to `main` and `develop`. Node 24 runs `npm ci`, lint, typecheck, Vitest and regression tests, and a production build. Builds use no Atlas credentials; database regression checks use disposable loopback MongoDB. + +10. **Milestone 3 release.** The [plan](milestone-3-plan.md), [deployment guide](milestone-3-deployment.md), and [evidence record](milestone-3-evidence.md) describe protected management, live verification, and developer contributions. HTTP Basic access is enforced in `src/proxy.ts`, the server management page, and every write handler. +11. **Tests.** `npm test` runs Vitest unit/component checks and the retained Node regression suite. The HTTP harness starts its own Next.js server with separate output and a seeded disposable MongoDB database. `npm run typecheck` checks TypeScript. See [the Offering contract](./offering-api-contract.md) for current and historical coverage boundaries. + +The [test handoff](../tests/unit/README.md) gives Vedika a starting point on `main`. +Reference tests on this release branch do not count as her personal contributions. +The retained `npm run test:storage` command covers storage and deliberate seed +execution from Naomi's merged work; use `npm run db:seed -- --apply` to seed. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..75ec4ac --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,13 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import prettier from "eslint-config-prettier"; +import prettierPlugin from "eslint-plugin-prettier"; + +export default defineConfig([ + ...nextVitals, + prettier, + { plugins: { prettier: prettierPlugin }, rules: { "prettier/prettier": ["error", { endOfLine: "auto" }] } }, + // These pages deliberately fetch external API data in an effect. + { rules: { "react-hooks/set-state-in-effect": "off" } }, + globalIgnores([".next/**", ".next-http-test/**", "coverage/**", "node_modules/**", "next-env.d.ts"]), +]); diff --git a/package-lock.json b/package-lock.json index 93e1e34..06fe2ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,20 +8,22 @@ "name": "nextjs-app-template", "version": "0.1.0", "dependencies": { - "mongoose": "^8", - "next": "^14", - "react": "^18", - "react-dom": "^18" + "mongoose": "^8.24.5", + "next": "^16.4.0", + "react": "^19.3.0", + "react-dom": "^19.3.0" }, "devDependencies": { "@testing-library/dom": "^10.4.2", + "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.3", - "@types/node": "^20.19.43", - "@types/react": "^18", - "@types/react-dom": "^18", + "@testing-library/user-event": "^14.6.7", + "@types/node": "^24", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", "esbuild": "^0.28.2", - "eslint": "^8", - "eslint-config-next": "^14", + "eslint": "^9.39.5", + "eslint-config-next": "^16.4.0", "eslint-config-prettier": "^9", "eslint-plugin-prettier": "^5", "husky": "^9", @@ -31,10 +33,19 @@ "prettier": "^3", "tsx": "^4.23.15", "typescript": "^5", - "vite": "^6.4.4", - "vitest": "^3.2.7" + "vitest": "^5.0.3" + }, + "engines": { + "node": "24.x" } }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@asamuzakjp/css-color": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", @@ -64,6 +75,176 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "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/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/@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.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@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.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "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/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/@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.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", @@ -74,6 +255,46 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.29.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", @@ -84,6 +305,54 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -199,6 +468,16 @@ "node": ">=18" } }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", @@ -642,74 +921,185 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "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.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "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": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "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/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "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": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@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.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -725,686 +1115,1188 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": 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==", + "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, - "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" - }, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=12" + "node": ">=20.9.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", - "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", - "dependencies": { - "sparse-bitfield": "^3.0.3" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.4" } }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", "cpu": [ "x64" ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": "^22.20 || ^24.12 || >=25" + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.4" } }, - "node_modules/@next/env": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.15.tgz", - "integrity": "sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.15.tgz", - "integrity": "sha512-pKU0iqKRBlFB/ocOI1Ip2CkKePZpYpnw5bEItEkuZ/Nr9FQP1+p7VDWr4VfOdff4i9bFmrOaeaU1bFEyAcxiMQ==", - "dev": true, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], "dependencies": { - "glob": "10.3.10" + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.15.tgz", - "integrity": "sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==", + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", "cpu": [ "arm64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz", - "integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==", + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", "cpu": [ "x64" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz", - "integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==", + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", "cpu": [ - "arm64" + "arm" + ], + "libc": [ + "glibc" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz", - "integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==", + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz", - "integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==", + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", "cpu": [ - "x64" + "ppc64" + ], + "libc": [ + "glibc" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz", - "integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==", + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", "cpu": [ - "x64" + "riscv64" + ], + "libc": [ + "glibc" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz", - "integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==", + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", "cpu": [ - "arm64" + "s390x" ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ - "win32" + "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz", - "integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==", + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", "cpu": [ - "ia32" + "x64" + ], + "libc": [ + "glibc" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ - "win32" + "linux" ], - "engines": { - "node": ">= 10" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz", - "integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==", + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", "cpu": [ - "x64" + "arm64" + ], + "libc": [ + "musl" ], + "license": "LGPL-3.0-or-later", "optional": true, "os": [ - "win32" + "linux" ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "engines": { - "node": ">=12.4.0" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "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, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, + "os": [ + "linux" + ], "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/libvips" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.5.tgz", - "integrity": "sha512-MrfgRQSpD5hkkRg6XI16B6EDLb+6+xp8xje6bpdSwkYw+qxrBKbUVrRAQu9h82UT+QumSIVQ8HXsXjPd2dlhqw==", + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", "cpu": [ "arm" ], - "dev": true, - "license": "MIT", + "libc": [ + "glibc" + ], + "license": "Apache-2.0", "optional": true, "os": [ - "android" - ] + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.4" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.5.tgz", - "integrity": "sha512-+uu2p/R9Tzn8GpoQyX1pAo5dmKT1DhXevfaCzqzmPFhnLjHLWw7uWkaVGlxmhwYoRiYYhsGfRcD5cTBpL7rgSw==", + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", "cpu": [ "arm64" ], - "dev": true, - "license": "MIT", + "libc": [ + "glibc" + ], + "license": "Apache-2.0", "optional": true, "os": [ - "android" - ] + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.4" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.5.tgz", - "integrity": "sha512-17dys+YEmzwzzFVHQAVDONg5FALXY9kd99HFEpfk4gJVHYxoh5bGea3D4t1fXXnwkTcOe25wPyd6nWS0I8IdLQ==", + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", "cpu": [ - "arm64" + "ppc64" ], - "dev": true, - "license": "MIT", + "libc": [ + "glibc" + ], + "license": "Apache-2.0", "optional": true, "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.5.tgz", - "integrity": "sha512-wg0zQEKkEvFZf1UaaX1UELOoPs//OSKUG7D+yITuqOsAvza7LvHTv5Wu4nmMfzNG7EYI2jp2PJC7Id6OETmsYQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.5.tgz", - "integrity": "sha512-CoJmWcpEMuwin+TZ/ev/H9uDyF/uxUKQSWXdmIPn/UBx/uGV9+4ebMp0FwnlhWR9PE1AdzYsKf3bZevhif38+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.5.tgz", - "integrity": "sha512-PxZnLAaTUAL5xG0W2BXLeYPKrK2b3LIASV2/e/Kf7PZvSLI1iAQIm9B9aJD+YyFIT7hc6qaMV3+OOO35R28LfA==", - "cpu": [ - "x64" + "linux" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.4" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.5.tgz", - "integrity": "sha512-uPo3IvnONjhbFXwWgroBeFwOicuhT7ZwQrnzkzmOtBJzeQbkUJUE1SJDfDI31ijQ5+ZUIA6sPrdUMxTSnxUWAg==", + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", "cpu": [ - "arm" + "riscv64" ], - "dev": true, "libc": [ "glibc" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.4" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.5.tgz", - "integrity": "sha512-ywI8Jyuc4LyHYTjC0mw4yA1ulvZBkHusdpWnoiEBsmBuHvSnt90btTBGCZ+s/2y+YNNfVAEKy33nPo5UI9zTmg==", + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", "cpu": [ - "arm" + "s390x" ], - "dev": true, "libc": [ - "musl" + "glibc" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.4" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.5.tgz", - "integrity": "sha512-tmuXJoQ/3ovSr637aykZMdQteU/CB2APAz2CrvsbibuoHZi1BS1Db13cVViBdEARGwXCLn1DSJ8wsjoThLJPGA==", + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", "cpu": [ - "arm64" + "x64" ], - "dev": true, "libc": [ "glibc" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.4" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.5.tgz", - "integrity": "sha512-5FMjSYNcUOHx7oW8Qu9KB6jGPpvF/8sG7+BDHwU/Ihh1/6FMJ9ySmsFG4tJ1qbNRuP8OLzCBDso4sF0mQaLuFQ==", + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", "cpu": [ "arm64" ], - "dev": true, "libc": [ "musl" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.5.tgz", - "integrity": "sha512-K8ZJ+neLDbUkIrL8dqF/5LtuVCrKwo27kREgOlxjBEjlOXwHvDnHLU8Z/UKv/B49qi7e8hsSDOyoJwhwqhvX4w==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.5.tgz", - "integrity": "sha512-NlVxsq7PFPv0DBKGuNWRDKixQYQCjM53zl04mxEcKMBmJbCBIUm1JdPfIWDRHyOOFzzYPmLQzfeEwh4716ayQg==", + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", "cpu": [ - "loong64" + "x64" ], - "dev": true, "libc": [ "musl" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.5.tgz", - "integrity": "sha512-ARhRHJYs5eCFet5azfrHD97vOkD8OzNW2RSMlvo5CcBqXaTOLfp0OHDtUQh7/K5AL5fUYoFp2fgC9KDg4vVYMQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.5.tgz", - "integrity": "sha512-atSgreM5C7jpxd9aEXN+6vJ5wTd05iYw+S3FiQpw7VsoEl8B90llbq35ARON+GOHj5QlVQEq2vOKWCHTob/BSQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", + "node_modules/@img/sharp-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.5.tgz", - "integrity": "sha512-vmlmwBFGnjVWxSRoVabNkyYuscSmg52Pd0fM8jEbNT3Jmka4A6HcB84x4uAmuumznWfBVaxKOqU6jbEgomWKCA==", + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" + "wasm32" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.5.tgz", - "integrity": "sha512-hhJrP6S9WkbsbcHgUkvCBS5Cn8IdsG8XjlLZyxjnv9+NdoYfbbZE4TWJYr8apdaj1M5t5Y1id2StnEslxpNFyA==", + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" + "arm64" ], - "license": "MIT", + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ - "linux" - ] + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.5.tgz", - "integrity": "sha512-NNVCyN9pGrwFmQa4bf+Cnf5aVH3g6ZpnlOg1Cs/H9swIHT9bRixijfzUqyEIWm1WtiAuR1LJ+rJH6zpBm8kgQA==", + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" + "ia32" ], - "license": "MIT", + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ - "linux" - ] + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.5.tgz", - "integrity": "sha512-dR9nd6OKnvSS60Ifz/L7Mj5zzYyoqyRn2NwTuaOONKaa3xsVGXyp8YusMbvFyEOR94DxnwgcbgsFqkHd29ZSzQ==", + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", "cpu": [ "x64" ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.5.tgz", - "integrity": "sha512-1tzsMjaqhsh3bAO6SJiJqh8hIr9sSPRd+i2vPPxJHod8/XYnf9TEnK0z/u3r1z1AkI347X0KbRSg9dWcxeGahw==", - "cpu": [ - "x64" + "win32" ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "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, - "libc": [ - "musl" - ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.5.tgz", - "integrity": "sha512-01mWMQblKoXXsmk28KXtpbA96w7g/aOWUoV3AC16fdHdqkykGalqyyM+5u8M7uP5/MEMiiPv69Hv7iv/hEH/dA==", - "cpu": [ - "x64" - ], + "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", - "optional": true, - "os": [ - "openbsd" - ] + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.5.tgz", - "integrity": "sha512-trdnMPb/DNm4V/CgsaoGgr90HfQQ071wfK0KoIs4YpLvjBgVQGXhGabg2Z6nK4EcYNilrb9ZZPcsP6+zpJIdxQ==", - "cpu": [ - "arm64" - ], + "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", - "optional": true, - "os": [ - "openharmony" - ] + "engines": { + "node": ">=6.0.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.5.tgz", - "integrity": "sha512-x2LxZlfdfsw99T6/qb9EqE/Qi84JWzibjyOJyhJsgFJy6CafaTiqIXCCnslCGrV5XWNh7fwrS3FbRENcuKnooA==", - "cpu": [ - "arm64" - ], + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.5.tgz", - "integrity": "sha512-/Qd/9aDQIo0+jmItgBmPIs8195YVbDxRRApn47GyufgbutCDOh7eP5vSKKlI4QNFeaI+wKQAkSMsPnF3EuMpaA==", - "cpu": [ - "ia32" - ], + "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", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.5.tgz", - "integrity": "sha512-ngUJ/XvOWrqbd2IMsfXfh2xXFLTP3sY7sCKvxbGvYdBZ1KmXFd3ewDZ9n3/GNzAUgzRuhLT/UvJRivbNoAQRBw==", - "cpu": [ + "node_modules/@mongodb-js/saslprep": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.6.0.tgz", + "integrity": "sha512-19jGXfpctLDJUELLUZGJ5BllcLT7rHdF+gTTaN2jzqu0bGkUhpdcYtgK2Vux+g3zuBAa0TM/W666QMIjYhYujw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@next/env": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.4.0.tgz", + "integrity": "sha512-pyLlT5oafGlX8X7xjgGNXz5dNIDSxREm+qD6i9VLSZiZFopA/XWl8NM/KPmLx/8EG/++c49iid3gNWSQW1+2dQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.4.0.tgz", + "integrity": "sha512-ffeflB/dL8RrvWejQximw01pOpKHN692Y3FjDB82DCd0YVvXvBD6vY7mcZp27yvSVFVM6wA73xVodEKF4spizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "4.9.1", + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/eslint-plugin-next/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/@next/eslint-plugin-next/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/@next/eslint-plugin-next/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.4.0.tgz", + "integrity": "sha512-yyZ+9MgViAKLLb5l1gPBGjRwnq3teNLb5Zx6JEw8LzNSTjlsnp+baUHFxTgl7PpjCmKf48VQfyipG1eBflb48Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.4.0.tgz", + "integrity": "sha512-RePEEejEj1Auf5s9qEcG0JbcDP3ceNUS/ofNX8AhxzgIjTVlZIHMatlp16YplcSMpsVo/ZMQHJTjbMYJznjGUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.4.0.tgz", + "integrity": "sha512-HhsyCqd+NpmvDDVY/LqkAN24f+QVvXh3ie+PfcwKinWL2Mu826JymQxJXGjAIfmJ4VlHjp4Pe/nZtXtUoEMAqw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.4.0.tgz", + "integrity": "sha512-sN9Pf+Zh+RpZAOmbR7d8KAA8T61CVhawtbEJ2Bk6QrRzx6Z/6haaF4UiwBzytM96GRRRWZh9/WALIEO3npMamA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.4.0.tgz", + "integrity": "sha512-D5M5xLWQVd1CvR+L7BickM1weRfmM41x3L1NWcnKY/oyJJqr1qwaCgmu3dMAhLBOMdZwSuDCRV6DJRRpiUkbDQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.4.0.tgz", + "integrity": "sha512-8d5WLS6a9YalqpOgXdlbvonFu+sIgjA+CO6tGzetrpPVcUL56foa7B7664h41rAlbLi5fJwK4vWji3zf4QDy+w==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.4.0.tgz", + "integrity": "sha512-sQuOIiW3snb48UwrBcDMvFA/u03qtw2LeuSIxFIQ9AJuW9UG/4Ddsfo+eV73PnZwBhyncMtsKVh7PrzMol2/fA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.4.0.tgz", + "integrity": "sha512-6/33lCuxpdHUy27RegyGM/5uTpF7YZd3UR4D0gpkOA/tWY5YR7msbQjqPgCZmSmxrrTLGCXYA4A8vT2H5DPGTQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.13.tgz", + "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.13.tgz", + "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.13.tgz", + "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.13.tgz", + "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.13.tgz", + "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.13.tgz", + "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.13.tgz", + "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.13.tgz", + "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.13.tgz", + "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.13.tgz", + "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.13.tgz", + "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.13.tgz", + "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==", + "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.13.tgz", + "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.13.tgz", + "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ] + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.5.tgz", - "integrity": "sha512-IhV6ew/DuzUWwHaYmlh7MrPCV3lKFUq/amTEwysi4r/mwNPAEfAMh5dYWeTSPTeMy2pFHGMEWYtPkHzfX3tG5A==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.13.tgz", + "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==", "cpu": [ "x64" ], @@ -1413,7 +2305,19 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT", + "peer": true }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -1421,24 +2325,13 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", - "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", - "dev": true - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" - }, "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" + "tslib": "^2.8.0" } }, "node_modules/@testing-library/dom": { @@ -1471,6 +2364,42 @@ "dequal": "^2.0.3" } }, + "node_modules/@testing-library/jest-dom": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.1.tgz", + "integrity": "sha512-oMDTC3oA+6CXSO2JZnvOI7CA6oVub6kij5ggk9ohwye5slmkwxYDXcPOVxgMw/RQlticjtO0C1RZkR97HgrWMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=22", + "npm": ">=6", + "yarn": ">=1" + }, + "peerDependencies": { + "@testing-library/dom": ">=10 <11", + "vitest": ">= 0.32" + }, + "peerDependenciesMeta": { + "vitest": { + "optional": true + } + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, "node_modules/@testing-library/react": { "version": "16.3.3", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz", @@ -1499,6 +2428,20 @@ } } }, + "node_modules/@testing-library/user-event": { + "version": "14.6.7", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.7.tgz", + "integrity": "sha512-MPCpX8bxe8zS+JmmTwLp8jd0dy1rAm60Te/SL8JrQM3qvQJcBOs1d7IefJMyZzqM3EWBrDn/LWDt1BCGu4ASfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -1531,1152 +2474,762 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.19.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", - "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true - }, - "node_modules/@types/react": { - "version": "18.3.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", - "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" - }, - "node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz", - "integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/type-utils": "8.10.0", - "@typescript-eslint/utils": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.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.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz", - "integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", - "debug": "^4.3.4" - }, - "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" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz", - "integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.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/type-utils": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz", - "integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "8.10.0", - "@typescript-eslint/utils": "8.10.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz", - "integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==", - "dev": true, - "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.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz", - "integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/visitor-keys": "8.10.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz", - "integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.10.0", - "@typescript-eslint/types": "8.10.0", - "@typescript-eslint/typescript-estree": "8.10.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" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz", - "integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "8.10.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/@vitest/expect": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", - "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "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", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.7", - "@vitest/utils": "3.2.7", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } + "license": "MIT" }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", - "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.1.tgz", + "integrity": "sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "undici-types": ">=7.24.0 <7.24.7" } }, - "node_modules/@vitest/runner": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", - "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.7", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "csstype": "^3.2.2" } }, - "node_modules/@vitest/snapshot": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", - "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", "dev": true, "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.7", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "peerDependencies": { + "@types/react": "^19.3.0" } }, - "node_modules/@vitest/spy": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", - "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", - "dev": true, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", "license": "MIT", "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "@types/webidl-conversions": "*" } }, - "node_modules/@vitest/utils": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", - "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.1.tgz", + "integrity": "sha512-nNlBf6HuotqMgZvSkWD5hNdemiBIxQ2NmrlgfcUIeZRs+BbakjscCQRq/Hb74EFVnFiSWWdL12JruqwBy3ERDw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.7", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.71.1", + "@typescript-eslint/type-utils": "8.71.1", + "@typescript-eslint/utils": "8.71.1", + "@typescript-eslint/visitor-keys": "8.71.1", + "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": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", - "dev": true, - "bin": { - "acorn": "bin/acorn" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "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, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^8.71.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.12.tgz", + "integrity": "sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "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": ">= 4" } }, - "node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "node_modules/@typescript-eslint/parser": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.1.tgz", + "integrity": "sha512-oM/5sAqz/l1v/zYK1qTOPrn2+JIrBGC1V6uNYucRO5gh6eT9vWJ/RhPbGE59byvZEUQ4VP6XkLBaS5Jti5jpvw==", "dev": true, + "license": "MIT", "dependencies": { - "environment": "^1.0.0" + "@typescript-eslint/scope-manager": "8.71.1", + "@typescript-eslint/types": "8.71.1", + "@typescript-eslint/typescript-estree": "8.71.1", + "@typescript-eslint/visitor-keys": "8.71.1", + "debug": "^4.4.3" }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "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, - "engines": { - "node": ">=8" + "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/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "ms": "^2.1.3" }, "engines": { - "node": ">=8" + "node": ">=6.0" }, - "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 - }, - "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.1.tgz", + "integrity": "sha512-Oijc9RUsohHjncg6iGi7gCtjnEjYt9FWv9u6ygK8uKcHK5eLjLAkqERQ9BaytbpceOFdk6kifPCS/zAZE+A99g==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "@typescript-eslint/tsconfig-utils": "^8.71.1", + "@typescript-eslint/types": "^8.71.1", + "debug": "^4.4.3" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "node_modules/@typescript-eslint/project-service/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.1.tgz", + "integrity": "sha512-1YUDZXdTnXLlob03SFHBipdyMBsDVm7zoSz0ytTJ39dk5J1TgsTK03VlR/dLXG/RLLUrPcYbD4rp23BmHxHP0w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "@typescript-eslint/types": "8.71.1", + "@typescript-eslint/visitor-keys": "8.71.1" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.1.tgz", + "integrity": "sha512-oTvkml5SxXhgg+WWKhod5qWQICDXtOB+/Hi9VdPKFAANJ2b7YOgMffgvzXuqog7R6A/JtrzRf128dsxummyWhg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.1.tgz", + "integrity": "sha512-kH3t3jZCYv2PZSBEWPKTariLrCaoF1CLoPitYFBJOhAGEwWnSRAtGEifrfrpBa4K9JlwWNZotgA5w3/wLSk/FQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "@typescript-eslint/types": "8.71.1", + "@typescript-eslint/typescript-estree": "8.71.1", + "@typescript-eslint/utils": "8.71.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "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/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "node_modules/@typescript-eslint/types": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.1.tgz", + "integrity": "sha512-ybYnPTUwg3VQQOFEiBPLs8Jxl1ry2vXdn1kVKguomBbvxpetdu3MKTNDqFNUrlG8ylP9MpZosmXMFYlGQHk1oQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.1.tgz", + "integrity": "sha512-pSKSEK1JJpHs6KiKVtABCE4iJK5d7FLFzDXQNBQ2oRY7LEd3UwOt3TZemTgu9syTIf56dJWlTn/pdmrO9oKUXQ==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "@typescript-eslint/project-service": "8.71.1", + "@typescript-eslint/tsconfig-utils": "8.71.1", + "@typescript-eslint/types": "8.71.1", + "@typescript-eslint/visitor-keys": "8.71.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "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": ">=12" + "node": "18 || 20 || >=22" } }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true - }, - "node_modules/async-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", - "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.4.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "possible-typed-array-names": "^1.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/axe-core": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.1.tgz", - "integrity": "sha512-qPC9o+kD8Tir0lzNGLeghbOrWMr3ZJpaRlCIb6Uobt/7N4FiEDvqUMnxzCHRHmg8vOg14kr5gVNyScRmbMaJ9g==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, "engines": { - "node": ">=4" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.1.tgz", + "integrity": "sha512-CBjT6gfAz3DW2j9Q4moFnqt208Aq+506sVGXfpRcyAeqn0EpayL0mjdWoCO2t3RC7GewNrFxdeehuEaPg6DMzQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.71.1", + "@typescript-eslint/types": "8.71.1", + "@typescript-eslint/typescript-estree": "8.71.1" + }, "engines": { - "node": ">= 0.4" - } - }, - "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 - }, - "node_modules/bare-events": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", - "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "bare-abort-controller": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } + "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/bare-fs": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.2.tgz", - "integrity": "sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.1.tgz", + "integrity": "sha512-0GxiUGqMU0qXJt58cBsiG3DP8lUmBaFo2vChtUX+CT7PnR3v/VGLLrzVP4K9r1lNiZXjHYefMBbPunplr09v6w==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" + "@typescript-eslint/types": "8.71.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "bare": ">=1.28.0" - }, - "peerDependencies": { - "bare-buffer": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/bare-path": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", - "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", + "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" + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/bare-stream": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", - "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", + "node_modules/@vitest/mocker": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.3.tgz", + "integrity": "sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "b4a": "^1.8.1", - "streamx": "^2.25.0", - "teex": "^1.0.1" + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.3", + "estree-walker": "^3.0.3", + "magic-string": "^1.2.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "bare-abort-controller": "*", - "bare-buffer": "*", - "bare-events": "*" + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - }, - "bare-buffer": { + "msw": { "optional": true }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-stream/node_modules/b4a": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", - "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { + "vite": { "optional": true } } }, - "node_modules/bare-url": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", - "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bare-path": "^3.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@vitest/spy": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.3.tgz", + "integrity": "sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" } }, - "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==", + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", "dev": true, - "dependencies": { - "fill-range": "^7.1.1" + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=8" - } - }, - "node_modules/bson": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", - "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", - "engines": { - "node": ">=16.20.1" + "node": ">=0.4.0" } }, - "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/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/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" + "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": { - "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, - "engines": { - "node": ">=6" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, - "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", - "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" - } - ] - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "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", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "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": "MIT", - "engines": { - "node": ">= 16" + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "dependencies": { + "deep-equal": "^2.0.5" } }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, + "license": "MIT", "dependencies": { - "restore-cursor": "^5.0.0" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "node_modules/array-includes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.2.0.tgz", + "integrity": "sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==", "dev": true, + "license": "MIT", "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "es-shim-unscopables": "^1.1.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "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==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/color-name": { + "node_modules/array.prototype.tosorted": { "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 - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "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 - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/cssstyle": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", - "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/data-urls/node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, "engines": { - "node": ">=18" + "node": ">= 0.4" } }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" + "tslib": "^2.4.0" } }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "possible-typed-array-names": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -2685,97 +3238,218 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "node_modules/axe-core": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.1.tgz", + "integrity": "sha512-qPC9o+kD8Tir0lzNGLeghbOrWMr3ZJpaRlCIb6Uobt/7N4FiEDvqUMnxzCHRHmg8vOg14kr5gVNyScRmbMaJ9g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, "engines": { "node": ">= 0.4" + } + }, + "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 + }, + "node_modules/bare-events": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "node_modules/bare-fs": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.2.tgz", + "integrity": "sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" }, "engines": { - "node": ">= 0.4" + "bare": ">=1.28.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } } }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/bare-path": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", + "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", + "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" }, - "engines": { - "node": ">=6.0" + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" }, "peerDependenciesMeta": { - "supports-color": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { "optional": true } } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "node_modules/bare-stream/node_modules/b4a": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", + "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.27.tgz", + "integrity": "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "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, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "node_modules/browserslist": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.3.tgz", + "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", "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.11.26", + "caniuse-lite": "^1.0.30001813", + "electron-to-chromium": "^1.5.439", + "node-releases": "^2.0.57", + "update-browserslist-db": "^1.3.3" + }, + "bin": { + "browserslist": "cli.js" + }, "engines": { - "node": ">=6" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -2784,38 +3458,29 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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 - }, - "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==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "function-bind": "^1.1.2" }, "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==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -2824,78 +3489,134 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "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/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, + "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "node_modules/caniuse-lite": { + "version": "1.0.30001815", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001815.tgz", + "integrity": "sha512-uDybUEyfgbyJJ93J1vikTylBX31AkdVYeVPeffMQpf8XN7hSfr0oZOn4VFOIMNvtU5+rHvC5Udg2TgtJKHm23Q==", + "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/chai": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, - "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 + "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, + "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/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 + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "license": "BSD-2-Clause", "engines": { - "node": ">=0.12" + "node": ">=12" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { "node": ">=18" }, @@ -2903,183 +3624,189 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "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, "dependencies": { - "get-intrinsic": "^1.2.4" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.4" + "node": ">=7.0.0" } }, - "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==", + "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 + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" + "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 + }, + "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/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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 8" } }, - "node_modules/es-iterator-helpers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", - "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.4", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "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==", "dev": true, "license": "MIT" }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, + "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-shim-unscopables": { + "node_modules/data-view-byte-length": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -3088,1268 +3815,1332 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" }, "engines": { - "node": ">=18" + "node": ">=6.0" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "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==", + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "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 + }, + "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==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-config-next": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.15.tgz", - "integrity": "sha512-mKg+NC/8a4JKLZRIOBplxXNdStgxy7lzWuedUaCc8tev+Al9mwDUTujQH6W6qXDH9kycWiVo28tADWGvpBsZcQ==", + "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==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "14.2.15", - "@rushstack/eslint-patch": "^1.3.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0", - "typescript": ">=3.3.1" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } + "license": "MIT" }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz", - "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==", + "node_modules/electron-to-chromium": { + "version": "1.5.451", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.451.tgz", + "integrity": "sha512-vUNKPrUzw8ByEMOxloq0a71iD/mPuPxVkbDm3G5YhnxTzOw/I6qAg2MUS0cYj/c267lWmw26yW4Vv7foxTxPLg==", + "dev": true, + "license": "ISC" + }, + "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 + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.3.5", - "enhanced-resolve": "^5.15.0", - "eslint-module-utils": "^2.8.1", - "fast-glob": "^3.3.2", - "get-tsconfig": "^4.7.5", - "is-bun-module": "^1.0.2", - "is-glob": "^4.0.3" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } + "node": ">=10.13.0" } }, - "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=4" + "node": ">=0.12" }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, + "license": "MIT", "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "is-core-module": "^2.15.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.0", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", - "tsconfig-paths": "^3.15.0" + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { - "node": ">=4" + "node": ">= 0.4" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "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==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz", - "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==", + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "dependencies": { - "aria-query": "~5.1.3", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.19", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.0" - }, - "engines": { - "node": ">=4.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "node_modules/es-iterator-helpers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", + "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" - }, - "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": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.4", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.3", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-react": { - "version": "7.37.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.1.tgz", - "integrity": "sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==", + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.19", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", - "string.prototype.repeat": "^1.0.0" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.0.0-canary-7118f5dd7-20230705", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", - "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "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, - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "engines": { + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "hasown": "^2.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "node_modules/es-to-primitive": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, + "hasInstallScript": true, + "license": "MIT", "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "esbuild": "bin/esbuild" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/eslint" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, - "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==", + "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": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=6" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "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, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "@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.6", + "@eslint/js": "9.39.5", + "@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": ">=0.10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "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==", + "node_modules/eslint-config-next": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.4.0.tgz", + "integrity": "sha512-2RHLYswzc+hNRktWAjIL6XAvwb1cKcjLawJ5KWywAxn/JivNHtG42ZigL1yobVXDqsimuuL2F9jcEcnHFzbppQ==", "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "@next/eslint-plugin-next": "16.4.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.1.0", + "globals": "16.4.0", + "typescript-eslint": "^8.56.0" }, - "engines": { - "node": ">=4.0" + "peerDependencies": { + "eslint": ">=9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.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==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true - }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "bare-events": "^2.7.0" + "ms": "^2.1.1" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz", + "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.3.5", + "enhanced-resolve": "^5.15.0", + "eslint-module-utils": "^2.8.1", + "fast-glob": "^3.3.2", + "get-tsconfig": "^4.7.5", + "is-bun-module": "^1.0.2", + "is-glob": "^4.0.3" }, "engines": { - "node": ">=16.17" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, - "node_modules/expect-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", - "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "node_modules/eslint-module-utils": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, "engines": { - "node": ">=12.0.0" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "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 - }, - "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 - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" + "ms": "^2.1.1" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" }, "engines": { - "node": ">= 6" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "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 - }, - "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 - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "reusify": "^1.0.4" + "ms": "^2.1.1" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "esutils": "^2.0.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=0.10.0" } }, - "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==", + "node_modules/eslint-plugin-import/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, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz", + "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==", "dev": true, - "license": "MIT", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "aria-query": "~5.1.3", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.19", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=4.0" }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "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==", + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" }, "engines": { - "node": ">=10" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "url": "https://opencollective.com/eslint-plugin-prettier" }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz", - "integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { - "debug": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { "optional": true } } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "node_modules/eslint-plugin-react": { + "version": "7.37.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.1.tgz", + "integrity": "sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.19", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { - "node": ">=14" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "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 - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "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": "^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==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "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 || ^10.0.0" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "esutils": "^2.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "node_modules/eslint-plugin-react/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, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "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": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "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": ">=16" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "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": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/get-tsconfig": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", - "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { - "resolve-pkg-maps": "^1.0.0" + "estraverse": "^5.1.0" }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "engines": { + "node": ">=0.10" } }, - "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "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": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4.0" } }, - "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==", + "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, - "dependencies": { - "is-glob": "^4.0.3" - }, "engines": { - "node": ">=10.13.0" + "node": ">=4.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@types/estree": "^1.0.0" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "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, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bare-events": "^2.7.0" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=16.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" } }, - "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==" + "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/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "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 }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "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==", + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, - "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==", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "es-define-property": "^1.0.0" + "is-glob": "^4.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 6" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "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, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "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 + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "reusify": "^1.0.4" } }, - "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==", + "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": { - "has-symbols": "^1.0.3" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16.0.0" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "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, "dependencies": { - "function-bind": "^1.1.2" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "license": "MIT", "dependencies": { - "whatwg-encoding": "^3.1.1" + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" }, "engines": { - "node": ">=18" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "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": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 14" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "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": { - "agent-base": "^7.1.2", - "debug": "4" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">= 14" + "node": ">=16" } }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz", + "integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", "engines": { - "node": ">=16.17.0" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/husky": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", - "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==", + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, - "bin": { - "husky": "bin.js" + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/typicode" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "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", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=0.10.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "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==", "dev": true, - "engines": { - "node": ">= 4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", "dev": true, + "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "engines": { - "node": ">=0.8.19" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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.", + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "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, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "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" @@ -4358,41 +5149,42 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" + "engines": { + "node": ">=16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4401,49 +5193,51 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-bun-module": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz", - "integrity": "sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==", + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, "dependencies": { - "semver": "^7.6.3" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" + "resolve-pkg-maps": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "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, "dependencies": { - "hasown": "^2.0.2" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 0.4" + "node": ">=10.13.0" + } + }, + "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/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "is-typed-array": "^1.1.13" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4452,14 +5246,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4467,71 +5259,51 @@ "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, - "engines": { - "node": ">=0.10.0" - } + "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 }, - "node_modules/is-finalizationregistry": { + "node_modules/has-bigints": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { + "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "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==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "dunder-proto": "^1.0.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -4539,11 +5311,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "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==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4551,22 +5324,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "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, "dependencies": { - "has-tostringtag": "^1.0.0" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -4575,1693 +5339,1874 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "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/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "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": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "hermes-estree": "0.25.1" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, - "engines": { - "node": ">= 0.4" + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 14" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 14" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.17.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/husky": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", + "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" + "bin": { + "husky": "bin.js" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "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, + "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": { - "which-typed-array": "^1.1.14" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.8.19" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/iterator.prototype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", - "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "url": "https://github.com/sponsors/ljharb" } }, - "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==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, + "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" + "has-bigints": "^1.0.2" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsdom/node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^2.3.1" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "node_modules/is-bun-module": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz", + "integrity": "sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==", "dev": true, - "license": "MIT", "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" + "semver": "^7.6.3" } }, - "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 - }, - "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 - }, - "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 - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" + "engines": { + "node": ">= 0.4" }, - "bin": { - "json5": "lib/cli.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "node_modules/is-core-module": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "hasown": "^2.0.4" }, "engines": { - "node": ">=4.0" - } - }, - "node_modules/kareem": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", - "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", - "engines": { - "node": ">=12.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, + "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, + "license": "MIT", "dependencies": { - "language-subtag-registry": "^0.3.20" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "call-bound": "^1.0.4" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "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, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "node": ">=0.10.0" } }, - "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.3.6", - "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", - "micromatch": "~4.0.8", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.5.0" + "call-bound": "^1.0.3" }, - "bin": { - "lint-staged": "bin/lint-staged.js" + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "engines": { - "node": ">=18.12.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/lint-staged" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/listr2": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", - "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", + "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, "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true + "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, + "engines": { + "node": ">=0.12.0" + } }, - "node_modules/listr2/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/listr2/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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 - }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, + "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.0.0" + "call-bound": "^1.0.3" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterator.prototype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", + "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", "dev": true, "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "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 + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" }, "engines": { "node": ">=18" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "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==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, + "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": { - "loose-envify": "cli.js" + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "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/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 - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "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", - "bin": { - "lz-string": "bin/bin.js" - } + "license": "MIT" }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "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 + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, - "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0" } }, - "node_modules/make-dir/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/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "engines": { + "node": ">=12.0.0" } }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + "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/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==", + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, "engines": { - "node": ">= 8" + "node": ">=0.10" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "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, "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=8.6" + "node": ">= 0.8.0" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, "engines": { - "node": ">=12" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { - "node": ">=18" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": "*" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mongodb": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz", - "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", - "mongodb-connection-string-url": "^3.0.0" - }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" + "node": ">= 12.0.0" }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mongodb-connection-string-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", - "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^13.0.0" + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mongodb-memory-server-core": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-10.4.3.tgz", - "integrity": "sha512-IPjlw73IoSYopnqBibQKxmAXMbOEPf5uGAOsBcaUiNH/TOI7V19WO+K7n5KYtnQ9FqzLGLpvwCGuPOTBSg4s5Q==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "async-mutex": "^0.5.0", - "camelcase": "^6.3.0", - "debug": "^4.4.3", - "find-cache-dir": "^3.3.2", - "follow-redirects": "^1.15.11", - "https-proxy-agent": "^7.0.6", - "mongodb": "^6.9.0", - "new-find-package-json": "^2.0.0", - "semver": "^7.7.3", - "tar-stream": "^3.1.7", - "tslib": "^2.8.1", - "yauzl": "^3.2.0" - }, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=16.20.1" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mongodb-memory-server-core/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=6.0" + "node": ">= 12.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/mongoose": { - "version": "8.7.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.7.2.tgz", - "integrity": "sha512-Ok4VzMds9p5G3ZSUhmvBm1GdxanbzhS29jpSn02SPj+IXEVFnIdfwAlHHXWkyNscZKlcn8GuMi68FH++jo0flg==", - "dependencies": { - "bson": "^6.7.0", - "kareem": "2.6.3", - "mongodb": "6.9.0", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "17.1.3" - }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=16.20.1" + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mongoose" + "url": "https://opencollective.com/parcel" } }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", - "dependencies": { - "debug": "4.x" + "node": ">= 12.0.0" }, - "engines": { - "node": ">=14.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "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==" - }, - "node_modules/nanoid": { - "version": "3.3.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", - "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "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 - }, - "node_modules/new-find-package-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", - "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { - "node": ">=12.22.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/next": { - "version": "14.2.15", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.15.tgz", - "integrity": "sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==", + "node_modules/lint-staged": { + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "dev": true, "dependencies": { - "@next/env": "14.2.15", - "@swc/helpers": "0.5.5", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", - "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" }, "bin": { - "next": "dist/bin/next" + "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": ">=18.17.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.15", - "@next/swc-darwin-x64": "14.2.15", - "@next/swc-linux-arm64-gnu": "14.2.15", - "@next/swc-linux-arm64-musl": "14.2.15", - "@next/swc-linux-x64-gnu": "14.2.15", - "@next/swc-linux-x64-musl": "14.2.15", - "@next/swc-win32-arm64-msvc": "14.2.15", - "@next/swc-win32-ia32-msvc": "14.2.15", - "@next/swc-win32-x64-msvc": "14.2.15" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.3.0" + "node": ">=18.12.0" }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "sass": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/lint-staged" } }, - "node_modules/npm-run-path": { + "node_modules/lint-staged/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "node_modules/listr2": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nwsapi": { - "version": "2.2.28", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.28.tgz", - "integrity": "sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==", - "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==", - "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", - "dev": true, - "engines": { - "node": ">= 0.4" - } + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "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, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "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 + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "dependencies": { - "wrappy": "1" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": true, "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" + "get-east-asian-width": "^1.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=18" + }, + "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==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "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==", + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "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==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "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==", + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "callsites": "^3.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "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==", "dev": true, - "license": "MIT", "dependencies": { - "entities": "^6.0.0" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "bin": { + "loose-envify": "cli.js" } }, - "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, - "engines": { - "node": ">=8" - } + "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 }, - "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==", + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" } }, - "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==", + "node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", "dev": true, - "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==", - "dev": true + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } }, - "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==", + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "semver": "^6.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "node_modules/make-dir/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": "MIT" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "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==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14.16" + "node": ">= 0.4" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "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==" + "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 }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, "engines": { "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, "engines": { - "node": ">=0.10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=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": { - "find-up": "^4.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" + "node": "*" } }, - "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==", + "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/mongodb": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "license": "Apache-2.0", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } } }, - "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==", + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongodb-memory-server-core": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-10.4.3.tgz", + "integrity": "sha512-IPjlw73IoSYopnqBibQKxmAXMbOEPf5uGAOsBcaUiNH/TOI7V19WO+K7n5KYtnQ9FqzLGLpvwCGuPOTBSg4s5Q==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "async-mutex": "^0.5.0", + "camelcase": "^6.3.0", + "debug": "^4.4.3", + "find-cache-dir": "^3.3.2", + "follow-redirects": "^1.15.11", + "https-proxy-agent": "^7.0.6", + "mongodb": "^6.9.0", + "new-find-package-json": "^2.0.0", + "semver": "^7.7.3", + "tar-stream": "^3.1.7", + "tslib": "^2.8.1", + "yauzl": "^3.2.0" }, "engines": { - "node": ">=8" + "node": ">=16.20.1" } }, - "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==", + "node_modules/mongodb-memory-server-core/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=6" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "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, + "node_modules/mongoose": { + "version": "8.24.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.5.tgz", + "integrity": "sha512-S1cvmzLhw5Xthv450UjInCBq2JreRvvyfGSA3FmDw8yThPxUbPAyJqtXMz0dNm8BBFm2ctRBOBKJxDPqM2sG0w==", "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.20.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" }, "engines": { - "node": ">=8" + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", "engines": { - "node": ">= 0.4" + "node": ">=4.0.0" } }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "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==" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "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==", + "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 + }, + "node_modules/new-find-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", + "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=12.22.0" } }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, + "node_modules/next": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/next/-/next-16.4.0.tgz", + "integrity": "sha512-N2C+9q89Z/oLrsepSRS4jRMtwmUZAoZuAHU0e9T6+sAB5DdhADmrnOW+zjYijlrYFmVfmQhGVGy9eMFoB7UgKg==", + "license": "MIT", + "dependencies": { + "@next/env": "16.4.0", + "@swc/helpers": "0.5.23", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.5.23", + "styled-jsx": "5.1.6" + }, "bin": { - "prettier": "bin/prettier.cjs" + "next": "dist/bin/next" }, "engines": { - "node": ">=14" + "node": ">=20.9.0" }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.4.0", + "@next/swc-darwin-x64": "16.4.0", + "@next/swc-linux-arm64-gnu": "16.4.0", + "@next/swc-linux-arm64-musl": "16.4.0", + "@next/swc-linux-x64-gnu": "16.4.0", + "@next/swc-linux-x64-musl": "16.4.0", + "@next/swc-win32-arm64-msvc": "16.4.0", + "@next/swc-win32-x64-msvc": "16.4.0", + "sharp": "^0.35.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "node_modules/node-releases": { + "version": "2.0.57", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz", + "integrity": "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==", "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, + "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "path-key": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "node_modules/nwsapi": { + "version": "2.2.28", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.28.tgz", + "integrity": "sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==", "dev": true, "license": "MIT" }, - "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==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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==", "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" - } - ] - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "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==", - "dev": true - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "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==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6269,16 +7214,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, "dependencies": { "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.2" + "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -6287,228 +7230,158 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "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==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "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==", "dev": true, - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { - "mimic-function": "^5.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "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" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.4" } }, - "node_modules/rollup": { - "version": "4.64.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.5.tgz", - "integrity": "sha512-EAP5ZHcz2ijbgCWwPkpI7zh6bfsri2Ez5FtCy2dCp1k62duXzfG7DLGGh7RU2QQXWwavT+u4qpvDF/TWOiiGTw==", + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.64.5", - "@rollup/rollup-android-arm64": "4.64.5", - "@rollup/rollup-darwin-arm64": "4.64.5", - "@rollup/rollup-darwin-x64": "4.64.5", - "@rollup/rollup-freebsd-arm64": "4.64.5", - "@rollup/rollup-freebsd-x64": "4.64.5", - "@rollup/rollup-linux-arm-gnueabihf": "4.64.5", - "@rollup/rollup-linux-arm-musleabihf": "4.64.5", - "@rollup/rollup-linux-arm64-gnu": "4.64.5", - "@rollup/rollup-linux-arm64-musl": "4.64.5", - "@rollup/rollup-linux-loong64-gnu": "4.64.5", - "@rollup/rollup-linux-loong64-musl": "4.64.5", - "@rollup/rollup-linux-ppc64-gnu": "4.64.5", - "@rollup/rollup-linux-ppc64-musl": "4.64.5", - "@rollup/rollup-linux-riscv64-gnu": "4.64.5", - "@rollup/rollup-linux-riscv64-musl": "4.64.5", - "@rollup/rollup-linux-s390x-gnu": "4.64.5", - "@rollup/rollup-linux-x64-gnu": "4.64.5", - "@rollup/rollup-linux-x64-musl": "4.64.5", - "@rollup/rollup-openbsd-x64": "4.64.5", - "@rollup/rollup-openharmony-arm64": "4.64.5", - "@rollup/rollup-win32-arm64-msvc": "4.64.5", - "@rollup/rollup-win32-ia32-msvc": "4.64.5", - "@rollup/rollup-win32-x64-gnu": "4.64.5", - "@rollup/rollup-win32-x64-msvc": "4.64.5", - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", "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" - } + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" ], - "dependencies": { - "queue-microtask": "^1.2.2" + "license": "MIT", + "engines": { + "node": ">=12.20.0" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "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/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -6517,348 +7390,405 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "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": "ISC", "dependencies": { - "xmlchars": "^2.2.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=v12.22.7" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "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, "dependencies": { - "loose-envify": "^1.1.0" + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "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": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" } }, - "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==", + "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": { - "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" + "callsites": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "entities": "^6.0.0" }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "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, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "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==", + "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, - "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==", + "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==", + "dev": true + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "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==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "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": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "find-up": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/sift": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { + "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "engines": { - "node": ">=14" + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=8" } }, - "node_modules/slice-ansi": { + "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "node": ">=8" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "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": ">=12" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/source-map-js": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", - "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "dependencies": { - "memory-pager": "^1.0.2" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "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": { - "internal-slot": "^1.0.4" + "p-limit": "^2.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/streamsearch": { + "node_modules/possible-typed-array-names": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" } }, - "node_modules/streamx": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", - "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", - "dev": true, + "node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "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": { - "events-universal": "^1.0.0", - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "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, "engines": { - "node": ">=0.6.19" + "node": ">= 0.8.0" } }, - "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==", + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=12" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "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==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/string-width-cjs/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 - }, - "node_modules/string-width-cjs/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==", + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "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": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "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==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" - }, + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + ] + }, + "node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "peerDependencies": { + "react": "^19.3.0" } }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "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==", + "dev": true + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -6867,26 +7797,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -6895,154 +7818,202 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "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, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "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==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "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==", + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, + "node_modules/rolldown": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz", + "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "js-tokens": "^9.0.1" + "@oxc-project/types": "=0.153.0", + "@rolldown/pluginutils": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.13", + "@rolldown/binding-android-arm64": "1.2.13", + "@rolldown/binding-darwin-arm64": "1.2.13", + "@rolldown/binding-darwin-x64": "1.2.13", + "@rolldown/binding-freebsd-x64": "1.2.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", + "@rolldown/binding-linux-arm64-gnu": "1.2.13", + "@rolldown/binding-linux-arm64-musl": "1.2.13", + "@rolldown/binding-linux-ppc64-gnu": "1.2.13", + "@rolldown/binding-linux-s390x-gnu": "1.2.13", + "@rolldown/binding-linux-x64-gnu": "1.2.13", + "@rolldown/binding-linux-x64-musl": "1.2.13", + "@rolldown/binding-openharmony-arm64": "1.2.13", + "@rolldown/binding-win32-arm64-msvc": "1.2.13", + "@rolldown/binding-win32-x64-msvc": "1.2.13" } }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", "dev": true, "license": "MIT" }, - "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], "dependencies": { - "client-only": "0.0.1" + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + "node": ">=0.4" }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, "engines": { "node": ">= 0.4" }, @@ -7050,353 +8021,376 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "license": "MIT" }, - "node_modules/synckit": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", - "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, + "license": "ISC", "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" + "xmlchars": "^2.2.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" + "node": ">=v12.22.7" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/tar-stream": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.2.tgz", - "integrity": "sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==", + "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==", "dev": true, - "license": "MIT", "dependencies": { - "b4a": "^1.9.0", - "bare-fs": "^4.8.2", - "fast-fifo": "^1.3.2", - "streamx": "^2.28.1" + "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/tar-stream/node_modules/b4a": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", - "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } + "engines": { + "node": ">= 0.4" } }, - "node_modules/teex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", - "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", "dependencies": { - "streamx": "^2.12.5" + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/text-decoder": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", - "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", - "dev": true, + "node_modules/sharp": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", "license": "Apache-2.0", + "optional": true, "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/text-decoder/node_modules/b4a": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", - "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" }, "peerDependenciesMeta": { - "react-native-b4a": { + "@types/node": { "optional": true } } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "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": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=8" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "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": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=8" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, "engines": { - "node": ">=14.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tinyspy": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz", - "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==", + "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==", "dev": true, "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": ">=14.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.86" + "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" }, - "bin": { - "tldts": "bin/cli.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT" + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" }, - "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==", + "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, - "dependencies": { - "is-number": "^7.0.0" - }, "engines": { - "node": ">=8.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "tldts": "^6.1.32" + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" }, "engines": { - "node": ">=16" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dependencies": { - "punycode": "^2.3.0" - }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { - "node": ">=14" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" + "node": ">=0.10.0" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "memory-pager": "^1.0.2" } }, - "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/std-env": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", + "dev": true, + "license": "MIT" }, - "node_modules/tsx": { - "version": "4.23.15", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", - "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" }, "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "node": ">= 0.4" } }, - "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==", + "node_modules/streamx": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.6.19" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, "dependencies": { "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" }, "engines": { "node": ">= 0.4" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "dev": true, "dependencies": { "call-bind": "^1.0.7", - "for-each": "^0.3.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -7405,18 +8399,31 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7425,18 +8432,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" }, "engines": { "node": ">= 0.4" @@ -7445,664 +8451,655 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "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/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, - "dependencies": { - "punycode": "^2.1.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vite": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.4.tgz", - "integrity": "sha512-xGjU/SyjeNyb0E4KxXBvYz/1C+3JXuujvf2hXNOtnlcE+58o5ajd9N2/n2VhkiJvpCP2qQasnUpx5PXNeI+p1Q==", + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" + "min-indent": "^1.0.0" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": ">=8" + } + }, + "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/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { "optional": true }, - "yaml": { + "babel-plugin-macros": { "optional": true } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "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": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" + "has-flag": "^4.0.0" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "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==", + "dev": true, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite-node/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, - "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.2.tgz", + "integrity": "sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.9.0", + "bare-fs": "^4.8.2", + "fast-fifo": "^1.3.2", + "streamx": "^2.28.1" + } + }, + "node_modules/tar-stream/node_modules/b4a": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" }, "peerDependenciesMeta": { - "supports-color": { + "react-native-b4a": { "optional": true } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" + "dependencies": { + "streamx": "^2.12.5" } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" } }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], + "node_modules/text-decoder/node_modules/b4a": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], + "node_modules/tinybench": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.1.tgz", + "integrity": "sha512-IAbQaPJIIdhVxi0mqy9lez1wEwje0UQf9F1vwtbelEG5Nbgn0nPOxCvU+mNeNS9gyJTAhrHp74CbUaSynQpZcQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=18" + "node": ">=20.0.0" } }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, "engines": { - "node": ">=18" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], + "node_modules/tinyglobby/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", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=18" + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT" + }, + "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, + "dependencies": { + "is-number": "^7.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, "engines": { - "node": ">=18" + "node": ">=16" } }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "punycode": "^2.3.1" + }, "engines": { "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], + "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", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], + "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/tsx": { + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], + "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", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "prelude-ls": "^1.2.1" + }, "engines": { - "node": ">=18" + "node": ">= 0.8.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], + "node_modules/typed-array-byte-offset": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.5.tgz", + "integrity": "sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.10" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=18" + "node": ">=14.17" } }, - "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], + "node_modules/typescript-eslint": { + "version": "8.71.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.71.1.tgz", + "integrity": "sha512-oiNVPC3/NbV1FpaHU07l8O6ynqG9v4PAiW2WvTrAmZdR0f2HI/gE668ElMLCK7Xbsu5k2fK5C4fXNiq05j7/nQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.71.1", + "@typescript-eslint/parser": "8.71.1", + "@typescript-eslint/typescript-estree": "8.71.1", + "@typescript-eslint/utils": "8.71.1" + }, "engines": { - "node": ">=18" + "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/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "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" + } ], - "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], + "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": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "node_modules/vite": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.3.tgz", + "integrity": "sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==", "dev": true, - "hasInstallScript": true, "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.11", + "tinyglobby": "^0.2.17" + }, "bin": { - "esbuild": "bin/esbuild" + "vite": "bin/vite.js" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/vite/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" + "fsevents": "~2.3.3" }, "peerDependencies": { - "picomatch": "^3 || ^4" + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { - "picomatch": { + "@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 } } @@ -8113,6 +9110,7 @@ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8140,6 +9138,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.19", "picocolors": "^1.1.1", @@ -8150,65 +9149,72 @@ } }, "node_modules/vitest": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", - "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.3.tgz", + "integrity": "sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.7", - "@vitest/mocker": "3.2.7", - "@vitest/pretty-format": "^3.2.7", - "@vitest/runner": "3.2.7", - "@vitest/snapshot": "3.2.7", - "@vitest/spy": "3.2.7", - "@vitest/utils": "3.2.7", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" + "@vitest/mocker": "5.0.3", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "^6.1.4", + "tinyexec": "^1.3.0", + "tinyglobby": "^0.2.17", + "why-is-node-running": "3.2.1" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.7", - "@vitest/ui": "3.2.7", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.3", + "@vitest/browser-preview": "5.0.3", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.3", + "@vitest/coverage-v8": "5.0.3", + "@vitest/ui": "5.0.3", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, - "@types/debug": { + "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, - "@vitest/browser": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { @@ -8219,51 +9225,9 @@ }, "jsdom": { "optional": true - } - } - }, - "node_modules/vitest/node_modules/@vitest/mocker": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", - "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.7", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true }, "vite": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true + "optional": false } } }, @@ -8326,15 +9290,16 @@ } }, "node_modules/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", "dependencies": { - "tr46": "^4.1.1", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/which": { @@ -8353,39 +9318,45 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, + "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -8413,15 +9384,18 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.24.tgz", + "integrity": "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -8432,20 +9406,16 @@ } }, "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", "dev": true, "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, "bin": { "why-is-node-running": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=20.11" } }, "node_modules/word-wrap": { @@ -8457,115 +9427,6 @@ "node": ">=0.10.0" } }, - "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, - "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/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, - "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/wrap-ansi-cjs/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 - }, - "node_modules/wrap-ansi-cjs/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-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==", - "dev": true - }, "node_modules/ws": { "version": "8.22.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", @@ -8605,6 +9466,13 @@ "dev": true, "license": "MIT" }, + "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/yaml": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", @@ -8641,6 +9509,29 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", + "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/package.json b/package.json index b7a904e..9e5c826 100644 --- a/package.json +++ b/package.json @@ -6,39 +6,42 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint --dir src --dir tests/unit --file vitest.config.mts", - "lint:fix": "next lint --fix", + "lint": "eslint src tests/unit scripts/*.ts vitest.config.mts", + "lint:fix": "npm run lint -- --fix", "format": "prettier --write .", "preview:offerings": "node scripts/offering-preview-server.mjs", "test": "npm run test:unit && npm run test:regression", - "test:unit": "vitest run", - "test:watch": "vitest", - "test:regression": "node --test --import tsx tests/offerings-http.test.mjs tests/database-checker.test.mjs tests/offering-form.test.mjs src/lib/offerings.test.ts tests/special-offering-control.test.mjs tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", - "preflight": "npm run lint && npm run typecheck && npm test && npm run build", - "test:storage": "node --test --import tsx tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", - "db:seed": "node --import tsx scripts/seed-offerings.ts", "test:db": "node --test tests/database-checker.test.mjs", "db:check": "node scripts/check-database.mjs", "db:check:write": "node scripts/check-database.mjs --write", "typecheck": "tsc --noEmit", "prepare": "husky", - "preview:special-offerings": "node scripts/special-offerings-preview.mjs" + "preview:special-offerings": "node scripts/special-offerings-preview.mjs", + "test:unit": "vitest run", + "test:watch": "vitest", + "test:regression": "node --test --import tsx tests/offerings-http.test.mjs tests/database-checker.test.mjs tests/offering-form.test.mjs src/lib/offerings.test.ts tests/special-offering-control.test.mjs tests/seed-offerings.test.mjs tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", + "test:storage": "node --test --import tsx tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", + "db:seed": "node --import tsx scripts/seed-offerings.ts", + "test:live": "node --import tsx scripts/verify-live.ts", + "preflight": "npm run lint && npm run typecheck && npm test && npm run build" }, "dependencies": { - "mongoose": "^8", - "next": "^14", - "react": "^18", - "react-dom": "^18" + "mongoose": "^8.24.5", + "next": "^16.4.0", + "react": "^19.3.0", + "react-dom": "^19.3.0" }, "devDependencies": { "@testing-library/dom": "^10.4.2", + "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.3", - "@types/node": "^20.19.43", - "@types/react": "^18", - "@types/react-dom": "^18", + "@testing-library/user-event": "^14.6.7", + "@types/node": "^24", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", "esbuild": "^0.28.2", - "eslint": "^8", - "eslint-config-next": "^14", + "eslint": "^9.39.5", + "eslint-config-next": "^16.4.0", "eslint-config-prettier": "^9", "eslint-plugin-prettier": "^5", "husky": "^9", @@ -48,7 +51,9 @@ "prettier": "^3", "tsx": "^4.23.15", "typescript": "^5", - "vite": "^6.4.4", - "vitest": "^3.2.7" + "vitest": "^5.0.3" + }, + "engines": { + "node": "24.x" } } diff --git a/scripts/live-acceptance.ts b/scripts/live-acceptance.ts new file mode 100644 index 0000000..0d92bd6 --- /dev/null +++ b/scripts/live-acceptance.ts @@ -0,0 +1,133 @@ +import { randomUUID } from "node:crypto"; +import { isOffering } from "@/lib/offering-response"; +import type { Offering } from "@/types/offering"; + +type Options = { + baseUrl: string; + username?: string; + password?: string; + allowWrites?: boolean; + allOperations?: boolean; + fetcher?: typeof fetch; +}; + +function assertPublicOffering(value: unknown): asserts value is Offering { + if (!isOffering(value) || Object.keys(value).sort().join(",") !== "ID,category,description,name,price,specialOffer") { + throw new Error("The server returned an invalid public offering."); + } +} + +export async function verifyLive({ + baseUrl, + username, + password, + allowWrites = false, + allOperations = false, + fetcher = fetch, +}: Options) { + const target = new URL(baseUrl); + const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(target.hostname); + if ( + (target.protocol !== "https:" && !(loopback && target.protocol === "http:")) || + target.username || + target.password || + target.search || + target.hash || + target.pathname !== "/" + ) { + throw new Error("Use an HTTPS origin, or a loopback HTTP origin, without paths or credentials."); + } + const request = (path: string, init: RequestInit = {}) => + fetcher(new URL(path, target), { + ...init, + redirect: "error", + signal: AbortSignal.timeout(15000), + }); + async function read(): Promise { + const response = await request("/api/offerings"); + if (response.status !== 200) throw new Error("GET offerings did not return 200."); + const records: unknown = await response.json(); + if (!Array.isArray(records)) throw new Error("GET offerings did not return an array."); + records.forEach(assertPublicOffering); + if (new Set(records.map((item) => item.ID)).size !== records.length) + throw new Error("GET returned duplicate identities."); + return records; + } + const baseline = await read(); + if (!allowWrites) return { readCount: baseline.length, writes: false }; + if (!username || !password || password.length < 16) + throw new Error("Configure private management credentials before write verification."); + const authorization = `Basic ${Buffer.from(`${username}:${password}`, "utf8").toString("base64")}`; + const headers = { Authorization: authorization, "Content-Type": "application/json" }; + const marker = randomUUID(); + const input = { + name: `Verification ${marker}`, + description: `Disposable milestone verification ${marker}`, + price: 3.75, + category: "Coffee", + specialOffer: true, + }; + const updatedInput = { ...input, name: `${input.name} edited`, specialOffer: false }; + const baselineIds = new Set(baseline.map((item) => item.ID)); + let creationAttempted = false; + let createdId: string | undefined; + try { + creationAttempted = true; + const denied = await request("/api/offerings", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(input), + }); + if (denied.status !== 401) throw new Error("Unauthenticated writes were not rejected with 401."); + const created = await request("/api/offerings", { method: "POST", headers, body: JSON.stringify(input) }); + if (created.status !== 201) throw new Error("Authorized creation did not return 201."); + const body: unknown = await created.json(); + assertPublicOffering(body); + if ( + baselineIds.has(body.ID) || + Object.entries(input).some(([key, value]) => body[key as keyof Offering] !== value) + ) { + throw new Error("Creation did not return the new offering details."); + } + createdId = body.ID; + const reread = (await read()).find((item) => item.ID === createdId); + if (!reread || Object.entries(body).some(([key, value]) => reread[key as keyof Offering] !== value)) { + throw new Error("Created offering was missing or changed on read-back."); + } + if (allOperations) { + const updated = await request(`/api/offerings/${encodeURIComponent(createdId)}`, { + method: "PUT", + headers, + body: JSON.stringify(updatedInput), + }); + if (updated.status !== 200) throw new Error("Updating the disposable offering did not return 200."); + const updatedBody: unknown = await updated.json(); + assertPublicOffering(updatedBody); + if ( + updatedBody.ID !== createdId || + Object.entries(updatedInput).some(([key, value]) => updatedBody[key as keyof Offering] !== value) + ) + throw new Error("Update returned incorrect offering details."); + const edited = (await read()).find((item) => item.ID === createdId); + if (!edited || Object.entries(updatedInput).some(([key, value]) => edited[key as keyof Offering] !== value)) + throw new Error("Updated offering did not persist on read-back."); + } + } finally { + if (creationAttempted) { + // Recover uncertain POST outcomes by marker; never delete pre-existing IDs. + const ownRecords = (await read()).filter( + (item) => + !baselineIds.has(item.ID) && + item.description === input.description && + [input.name, updatedInput.name].includes(item.name), + ); + for (const item of ownRecords) { + const removed = await request(`/api/offerings/${encodeURIComponent(item.ID)}`, { method: "DELETE", headers }); + if (removed.status !== 204) throw new Error(`Disposable cleanup failed. Check verification marker ${marker}.`); + } + if ((await read()).some((item) => ownRecords.some((own) => own.ID === item.ID))) + throw new Error(`Disposable cleanup did not persist. Check verification marker ${marker}.`); + } + } + return { readCount: baseline.length, writes: true, updated: allOperations }; +} diff --git a/scripts/verify-live.ts b/scripts/verify-live.ts new file mode 100644 index 0000000..4c99567 --- /dev/null +++ b/scripts/verify-live.ts @@ -0,0 +1,30 @@ +import { loadEnvConfig } from "@next/env"; +import { verifyLive } from "./live-acceptance"; + +async function main() { + try { + loadEnvConfig(process.cwd(), true, { info() {}, error() {} }); + const args = process.argv.slice(2); + if ( + args.some((arg) => !["--confirm-writes", "--all"].includes(arg)) || + (args.includes("--all") && !args.includes("--confirm-writes")) + ) + throw new Error("arguments"); + const result = await verifyLive({ + baseUrl: process.env.LIVE_BASE_URL ?? "", + username: process.env.MANAGEMENT_USERNAME, + password: process.env.MANAGEMENT_PASSWORD, + allowWrites: args.includes("--confirm-writes"), + allOperations: args.includes("--all"), + }); + console.log( + `PASS live: ${result.readCount} public offerings; ${result.writes ? "authorized persistence and targeted cleanup verified" : "read-only check"}${result.updated ? "; update verified" : ""}.`, + ); + } catch { + console.error( + "FAIL live: check LIVE_BASE_URL, private management credentials, and deployed behavior. Writes require --confirm-writes; --all also verifies updates. Check the deployment for disposable Verification records if a request or cleanup failed. No credentials printed.", + ); + process.exitCode = 1; + } +} +void main(); diff --git a/src/app/api/offerings/[id]/route.ts b/src/app/api/offerings/[id]/route.ts new file mode 100644 index 0000000..597cf2a --- /dev/null +++ b/src/app/api/offerings/[id]/route.ts @@ -0,0 +1,14 @@ +import { offeringStore } from "@/database/offeringStore"; +import { createOfferingHandlers } from "@/server/offering-handlers"; + +export const dynamic = "force-dynamic"; +export const runtime = "nodejs"; +const handlers = createOfferingHandlers(offeringStore); + +export async function PUT(request: Request, { params }: { params: Promise<{ id: string }> }) { + return handlers.PUT(request, (await params).id); +} + +export async function DELETE(request: Request, { params }: { params: Promise<{ id: string }> }) { + return handlers.DELETE(request, (await params).id); +} diff --git a/src/app/api/offerings/route.ts b/src/app/api/offerings/route.ts index a70e63a..143e0df 100644 --- a/src/app/api/offerings/route.ts +++ b/src/app/api/offerings/route.ts @@ -1,9 +1,9 @@ -import { NextResponse } from "next/server"; - -import { OfferingList } from "@/database/offering"; +import { offeringStore } from "@/database/offeringStore"; +import { createOfferingHandlers } from "@/server/offering-handlers"; export const dynamic = "force-dynamic"; -export async function GET() { - return NextResponse.json(OfferingList); -} +export const runtime = "nodejs"; +const handlers = createOfferingHandlers(offeringStore); +export const GET = handlers.GET; +export const POST = handlers.POST; diff --git a/src/app/favicon.ico b/src/app/favicon.ico deleted file mode 100644 index 4570eb8d9269ad58b17fecbec6d630cded56f507..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39535 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 diff --git a/src/app/globals.css b/src/app/globals.css index e69de29..4d70eea 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -0,0 +1,24 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: #fffdfb; + color: #2f241f; + font-family: Arial, Helvetica, sans-serif; + line-height: 1.5; +} + +main:not([class]) { + max-width: 56rem; + margin: 0 auto; + padding: 2rem 1rem; +} + +button, +input, +select, +textarea { + font: inherit; +} diff --git a/src/app/icon.svg b/src/app/icon.svg new file mode 100644 index 0000000..d2d1c65 --- /dev/null +++ b/src/app/icon.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/src/app/manage-offerings/page.tsx b/src/app/manage-offerings/page.tsx index dad91da..27a3006 100644 --- a/src/app/manage-offerings/page.tsx +++ b/src/app/manage-offerings/page.tsx @@ -1,193 +1,12 @@ -"use client"; +import { headers } from "next/headers"; +import { notFound } from "next/navigation"; +import ManageOfferings from "@/components/ManageOfferings"; +import { hasManagementAccess } from "@/server/management-access"; -import { useCallback, useEffect, useState } from "react"; -import OfferingForm, { type OfferingFormValues } from "@/components/OfferingForm"; -import { - fetchOfferings, - OfferingApiError, - parseOfferingPrice, - sortOfferings, - submitOffering, - validateOfferingInput, -} from "@/lib/offerings"; -import { DEFAULT_SPECIAL_OFFER, type CreateOfferingInput, type Offering } from "@/types/offering"; -import styles from "@/styles/manage-offerings.module.css"; +export const dynamic = "force-dynamic"; -const initialFormState: OfferingFormValues = { - name: "", - description: "", - price: "", - category: "Coffee", -}; - -export default function ManageOfferingsPage() { - const [offerings, setOfferings] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [loadError, setLoadError] = useState(""); - const [formValues, setFormValues] = useState(initialFormState); - const [specialOffer, setSpecialOffer] = useState(DEFAULT_SPECIAL_OFFER); - const [errors, setErrors] = useState>({}); - const [isSubmitting, setIsSubmitting] = useState(false); - const [feedback, setFeedback] = useState<{ - tone: "info" | "success" | "error"; - text: string; - } | null>(null); - - const loadOfferings = useCallback(async () => { - setIsLoading(true); - try { - const nextOfferings = await fetchOfferings(); - setOfferings(sortOfferings(nextOfferings)); - setLoadError(""); - } catch (error) { - const message = error instanceof Error ? error.message : "The offering list could not be loaded."; - setLoadError(message); - throw error; - } finally { - setIsLoading(false); - } - }, []); - - useEffect(() => { - void loadOfferings().catch(() => undefined); - }, [loadOfferings]); - - const updateField = (field: K, value: OfferingFormValues[K]) => { - setFormValues((current) => ({ ...current, [field]: value })); - setErrors((current) => ({ ...current, [field]: "" })); - }; - - const handleSubmit = async (event: React.FormEvent, submittedSpecialOffer: boolean) => { - event.preventDefault(); - if (isSubmitting) { - return; - } - - const submission: Partial = { - name: formValues.name, - description: formValues.description, - category: formValues.category, - price: parseOfferingPrice(formValues.price), - specialOffer: submittedSpecialOffer, - }; - - const nextErrors = validateOfferingInput(submission); - if (Object.keys(nextErrors).length > 0) { - setErrors(nextErrors); - setFeedback({ - tone: "error", - text: "Please fix the highlighted fields before saving.", - }); - return; - } - - const savedAttempt = { ...formValues }; - setIsSubmitting(true); - setFeedback({ tone: "info", text: "Saving offering..." }); - setErrors({}); - - try { - const createdOffering = await submitOffering(submission); - setOfferings((current) => sortOfferings([...current, createdOffering])); - setFormValues(initialFormState); - setSpecialOffer(DEFAULT_SPECIAL_OFFER); - setFeedback({ tone: "success", text: "Offering saved successfully." }); - - try { - await loadOfferings(); - } catch { - setFeedback({ - tone: "success", - text: "Offering saved, but the list could not refresh. Please reload the page to confirm the update.", - }); - } - } catch (error) { - setFormValues(savedAttempt); - if (error instanceof OfferingApiError) { - setErrors(error.fields); - } - setFeedback({ - tone: "error", - text: error instanceof Error ? error.message : "The offering could not be saved.", - }); - } finally { - setIsSubmitting(false); - } - }; - - return ( -

-
-

Manage offerings

-

Review current offerings, add new items, and confirm the latest list state.

-
- - {feedback ? ( -
- {feedback.text} -
- ) : null} - -
-
-

Current offerings

- - {isLoading ? ( -

Loading current offerings…

- ) : loadError ? ( -

{loadError}

- ) : offerings.length === 0 ? ( -
No offerings are available right now.
- ) : ( -
- {offerings.map((offering) => ( -
-

{offering.name}

-
- {offering.category} - {`$${offering.price.toFixed(2)}`} - {offering.specialOffer ? Special offer : null} -
-

{offering.description}

-
- ))} -
- )} -
- -
-

Add offering

- -
- setSpecialOffer(event.target.checked)} - /> - -
- - -
-
-
- ); +export default async function ManageOfferingsPage() { + // Independently enforce access here as well as in middleware. + if (!(await hasManagementAccess((await headers()).get("authorization")))) notFound(); + return ; } diff --git a/src/components/ManageOfferings.tsx b/src/components/ManageOfferings.tsx new file mode 100644 index 0000000..971b028 --- /dev/null +++ b/src/components/ManageOfferings.tsx @@ -0,0 +1,311 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import OfferingForm, { type OfferingFormValues } from "@/components/OfferingForm"; +import SpecialOfferingControl from "@/components/SpecialOfferingControl"; +import { + fetchOfferings, + OfferingApiError, + parseOfferingPrice, + sortOfferings, + submitOffering, + validateOfferingInput, + updateOffering, + deleteOffering, +} from "@/lib/offerings"; +import { DEFAULT_SPECIAL_OFFER, type CreateOfferingInput, type Offering } from "@/types/offering"; +import styles from "@/styles/manage-offerings.module.css"; + +const initialFormState: OfferingFormValues = { + name: "", + description: "", + price: "", + category: "Coffee", +}; + +export default function ManageOfferings() { + const [offerings, setOfferings] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [loadError, setLoadError] = useState(""); + const [formValues, setFormValues] = useState(initialFormState); + const [specialOffer, setSpecialOffer] = useState(DEFAULT_SPECIAL_OFFER); + const [errors, setErrors] = useState>({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const pending = useRef(false); + const [editingId, setEditingId] = useState(null); + const [deletingId, setDeletingId] = useState(null); + const [feedback, setFeedback] = useState<{ + tone: "info" | "success" | "error"; + text: string; + } | null>(null); + + const loadOfferings = useCallback(async () => { + setIsLoading(true); + try { + const nextOfferings = await fetchOfferings(); + setOfferings(sortOfferings(nextOfferings)); + setLoadError(""); + } catch (error) { + const message = error instanceof Error ? error.message : "The offering list could not be loaded."; + setLoadError(message); + throw error; + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + void loadOfferings().catch(() => undefined); + }, [loadOfferings]); + + const updateField = (field: K, value: OfferingFormValues[K]) => { + setFormValues((current) => ({ ...current, [field]: value })); + setErrors((current) => ({ ...current, [field]: "" })); + }; + + const handleSubmit = async (event: React.FormEvent, submittedSpecialOffer: boolean) => { + event.preventDefault(); + if (pending.current) { + return; + } + + const submission: Partial = { + name: formValues.name, + description: formValues.description, + category: formValues.category, + price: parseOfferingPrice(formValues.price), + specialOffer: submittedSpecialOffer, + }; + + const nextErrors = validateOfferingInput(submission); + if (Object.keys(nextErrors).length > 0) { + setErrors(nextErrors); + setFeedback({ + tone: "error", + text: "Please fix the highlighted fields before saving.", + }); + return; + } + + const savedAttempt = { ...formValues }; + pending.current = true; + setIsSubmitting(true); + setFeedback({ tone: "info", text: "Saving offering..." }); + setErrors({}); + + try { + const createdOffering = editingId + ? await updateOffering(editingId, submission) + : await submitOffering(submission); + setOfferings((current) => + sortOfferings([...current.filter((offering) => offering.ID !== createdOffering.ID), createdOffering]), + ); + setFormValues(initialFormState); + setSpecialOffer(DEFAULT_SPECIAL_OFFER); + setEditingId(null); + setFeedback({ + tone: "success", + text: editingId ? "Offering updated successfully." : "Offering saved successfully.", + }); + + try { + await loadOfferings(); + } catch { + setFeedback({ + tone: "success", + text: "Offering saved, but the list could not refresh. Please reload the page to confirm the update.", + }); + } + } catch (error) { + setFormValues(savedAttempt); + if (error instanceof OfferingApiError) { + setErrors(error.fields); + } + setFeedback({ + tone: "error", + text: error instanceof Error ? error.message : "The offering could not be saved.", + }); + } finally { + pending.current = false; + setIsSubmitting(false); + } + }; + + const cancelEdit = () => { + setEditingId(null); + setFormValues(initialFormState); + setSpecialOffer(DEFAULT_SPECIAL_OFFER); + setErrors({}); + }; + + const beginEdit = (offering: Offering) => { + setEditingId(offering.ID); + setFormValues({ + name: offering.name, + description: offering.description, + price: String(offering.price), + category: offering.category, + }); + setSpecialOffer(offering.specialOffer); + setErrors({}); + setFeedback(null); + setDeletingId(null); + document.getElementById("offering-name")?.focus(); + }; + + const confirmDelete = async (offering: Offering) => { + if (pending.current) return; + pending.current = true; + setIsSubmitting(true); + setFeedback({ tone: "info", text: "Deleting offering..." }); + try { + await deleteOffering(offering.ID); + setOfferings((current) => current.filter((item) => item.ID !== offering.ID)); + if (editingId === offering.ID) cancelEdit(); + setDeletingId(null); + setFeedback({ tone: "success", text: "Offering deleted successfully." }); + try { + await loadOfferings(); + } catch { + setFeedback({ + tone: "success", + text: "Offering deleted, but the list could not refresh. Please retry loading the list.", + }); + } + } catch (error) { + setFeedback({ + tone: "error", + text: error instanceof Error ? error.message : "The offering could not be deleted.", + }); + } finally { + pending.current = false; + setIsSubmitting(false); + } + }; + + return ( +
+
+

Manage offerings

+

Add, edit, and remove offerings shown on the Menu and Specials pages.

+
+ + {feedback ? ( +
+ {feedback.text} +
+ ) : null} + +
+
+

Current offerings

+ + {isLoading ? ( +

+ Loading current offerings… +

+ ) : loadError ? ( +
+

+ {loadError} +

+ +
+ ) : offerings.length === 0 ? ( +
No offerings are available right now.
+ ) : ( +
+ {offerings.map((offering) => ( +
+

{offering.name}

+
+ {offering.category} + {`$${offering.price.toFixed(2)}`} + {offering.specialOffer ? Special offer : null} +
+

{offering.description}

+
+ + +
+ {deletingId === offering.ID && ( +
+

Delete {offering.name}? This removes it from the customer pages.

+
+ + +
+
+ )} +
+ ))} +
+ )} +
+ +
+

{editingId ? "Edit offering" : "Add offering"}

+ + + + + {editingId && ( + + )} +
+
+
+ ); +} diff --git a/src/components/OfferingForm.tsx b/src/components/OfferingForm.tsx index 8f5f32a..19d6966 100644 --- a/src/components/OfferingForm.tsx +++ b/src/components/OfferingForm.tsx @@ -16,6 +16,7 @@ type OfferingFormProps = { onFieldChange: (field: K, value: OfferingFormValues[K]) => void; onSubmit: (event: React.FormEvent, specialOffer: boolean) => Promise; specialOffer?: boolean; + submitLabel?: string; }; export default function OfferingForm({ @@ -25,6 +26,7 @@ export default function OfferingForm({ onFieldChange, onSubmit, specialOffer = DEFAULT_SPECIAL_OFFER, + submitLabel = "Save offering", }: OfferingFormProps) { return (
onSubmit(event, specialOffer)} noValidate> @@ -116,7 +118,7 @@ export default function OfferingForm({
); diff --git a/src/components/OfferingsPage.tsx b/src/components/OfferingsPage.tsx index e4101d0..8f69f10 100644 --- a/src/components/OfferingsPage.tsx +++ b/src/components/OfferingsPage.tsx @@ -1,8 +1,9 @@ "use client"; import { useEffect, useState } from "react"; -import type { Offering, OfferingErrorResponse } from "@/types/offering"; +import type { Offering } from "@/types/offering"; import styles from "@/styles/offerings.module.css"; +import { isOffering, readOfferingError } from "@/lib/offering-response"; interface OfferingsPageProps { title: string; @@ -11,20 +12,11 @@ interface OfferingsPageProps { specialsOnly?: boolean; } -async function readErrorMessage(response: Response, fallback: string): Promise { - try { - const body = (await response.json()) as Partial; - const message = body?.error?.message; - return typeof message === "string" && message.trim() !== "" ? message : fallback; - } catch { - return fallback; - } -} - export default function OfferingsPage({ title, intro, emptyMessage, specialsOnly = false }: OfferingsPageProps) { const [offerings, setOfferings] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); + const [attempt, setAttempt] = useState(0); useEffect(() => { let isCurrent = true; @@ -38,12 +30,12 @@ export default function OfferingsPage({ title, intro, emptyMessage, specialsOnly const response = await fetch("/api/offerings"); if (!response.ok) { - throw new Error(await readErrorMessage(response, fallbackMessage)); + throw new Error((await readOfferingError(response, fallbackMessage)).message); } const data: unknown = await response.json(); - if (!Array.isArray(data)) { + if (!Array.isArray(data) || !data.every(isOffering)) { throw new Error(fallbackMessage); } @@ -67,7 +59,7 @@ export default function OfferingsPage({ title, intro, emptyMessage, specialsOnly return () => { isCurrent = false; }; - }, [title]); + }, [title, attempt]); const visibleOfferings = specialsOnly ? offerings.filter((offering) => offering.specialOffer === true) : offerings; @@ -81,6 +73,11 @@ export default function OfferingsPage({ title, intro, emptyMessage, specialsOnly {isLoading &&

Loading {title.toLowerCase()}...

} {error &&

{error}

} + {error && ( + + )} {!isLoading && !error && visibleOfferings.length === 0 &&

{emptyMessage}

} {!isLoading && !error && visibleOfferings.length > 0 && (
    diff --git a/src/components/SpecialOfferingControl.tsx b/src/components/SpecialOfferingControl.tsx index d181539..276a03d 100644 --- a/src/components/SpecialOfferingControl.tsx +++ b/src/components/SpecialOfferingControl.tsx @@ -6,13 +6,20 @@ import styles from "@/styles/special-offering-control.module.css"; type SpecialOfferingControlProps = { checked: boolean; onChange: (next: boolean) => void; + disabled?: boolean; }; -export default function SpecialOfferingControl({ checked, onChange }: SpecialOfferingControlProps) { +export default function SpecialOfferingControl({ checked, onChange, disabled = false }: SpecialOfferingControlProps) { const id = useId(); return ( ); diff --git a/src/database/db.ts b/src/database/db.ts index cb1df52..3e7f3ea 100644 --- a/src/database/db.ts +++ b/src/database/db.ts @@ -32,7 +32,13 @@ const connectDB = async (): Promise => { if (!cached.promise) { cached.promise = mongoose - .connect(url, { serverSelectionTimeoutMS: 5000, connectTimeoutMS: 5000, socketTimeoutMS: 10000 }) + .connect(url, { + serverSelectionTimeoutMS: 5000, + connectTimeoutMS: 5000, + socketTimeoutMS: 10000, + maxPoolSize: 5, + bufferCommands: false, + }) .then(() => mongoose); } diff --git a/src/database/offeringStore.ts b/src/database/offeringStore.ts index 73a7373..d5635d6 100644 --- a/src/database/offeringStore.ts +++ b/src/database/offeringStore.ts @@ -2,6 +2,7 @@ import connectDB from "@/database/db"; import OfferingModel, { type OfferingRecord } from "@/database/offeringModel"; import { sortOfferings } from "@/lib/offerings"; import type { CreateOfferingInput, Offering } from "@/types/offering"; +import type { OfferingStore } from "@/server/offering-handlers"; function publicOffering(record: OfferingRecord): Offering { return { @@ -15,7 +16,7 @@ function publicOffering(record: OfferingRecord): Offering { } /** Real storage boundary supplied to the separately developed request handlers. */ -export const offeringStore = { +export const offeringStore: OfferingStore = { async list(): Promise { await connectDB(); return sortOfferings((await OfferingModel.find().lean()).map(publicOffering)); @@ -25,4 +26,17 @@ export const offeringStore = { const { name, description, price, category, specialOffer } = input; return publicOffering(await OfferingModel.create({ name, description, price, category, specialOffer })); }, + async update(id, input) { + await connectDB(); + const record = await OfferingModel.findByIdAndUpdate( + id, + { $set: { ...input, specialOffer: input.specialOffer ?? false } }, + { new: true, runValidators: true }, + ); + return record ? publicOffering(record) : null; + }, + async remove(id) { + await connectDB(); + return (await OfferingModel.deleteOne({ _id: id })).deletedCount === 1; + }, }; diff --git a/src/lib/offering-response.ts b/src/lib/offering-response.ts new file mode 100644 index 0000000..d752812 --- /dev/null +++ b/src/lib/offering-response.ts @@ -0,0 +1,38 @@ +import { OFFERING_CATEGORIES, type Offering } from "@/types/offering"; + +/** Validate external responses before sorting or rendering them. */ +export function isOffering(value: unknown): value is Offering { + if (!value || typeof value !== "object") return false; + const item = value as Partial; + return ( + typeof item.ID === "string" && + item.ID.length > 0 && + typeof item.name === "string" && + item.name.trim().length > 0 && + typeof item.description === "string" && + typeof item.price === "number" && + Number.isFinite(item.price) && + item.price >= 0 && + OFFERING_CATEGORIES.some((category) => category === item.category) && + typeof item.specialOffer === "boolean" + ); +} +/** Parse only displayable fields from an untrusted API error response. */ +export async function readOfferingError(response: Response, fallback: string) { + const body: unknown = await response.json().catch(() => null); + const error = body && typeof body === "object" && "error" in body ? body.error : null; + if (!error || typeof error !== "object") return { message: fallback, fields: {} }; + const message = + "message" in error && typeof error.message === "string" && error.message.trim() ? error.message : fallback; + const rawFields = "fields" in error ? error.fields : null; + const fields: Record = {}; + if (rawFields && typeof rawFields === "object" && !Array.isArray(rawFields)) + for (const [key, value] of Object.entries(rawFields)) + if ( + ["name", "description", "price", "category", "specialOffer"].includes(key) && + typeof value === "string" && + value.trim() + ) + fields[key] = value; + return { message, fields }; +} diff --git a/src/lib/offerings.ts b/src/lib/offerings.ts index 6097c2d..1a331bd 100644 --- a/src/lib/offerings.ts +++ b/src/lib/offerings.ts @@ -8,6 +8,7 @@ import { type OfferingCategory, type OfferingErrorResponse, } from "@/types/offering"; +import { isOffering, readOfferingError } from "@/lib/offering-response"; export function isOfferingCategory(value: string): value is OfferingCategory { return OFFERING_CATEGORIES.some((category) => category === value); @@ -121,11 +122,12 @@ export function sortOfferings(offerings: Offering[]): Offering[] { export async function fetchOfferings(signal?: AbortSignal): Promise { const response = await fetch("/api/offerings", { method: "GET", signal }); if (!response.ok) { - const payload = (await response.json().catch(() => null)) as OfferingErrorResponse | null; - throw new Error(payload?.error?.message ?? "We could not load the current offerings."); + throw new Error((await readOfferingError(response, "We could not load the current offerings.")).message); } - const payload = (await response.json()) as Offering[]; + const payload: unknown = await response.json(); + if (!Array.isArray(payload) || !payload.every(isOffering)) + throw new Error("The offering list could not be read. Please try again."); return sortOfferings(payload); } @@ -140,21 +142,40 @@ export class OfferingApiError extends Error { } export async function submitOffering(input: Partial): Promise { + return saveOffering("/api/offerings", "POST", input); +} + +export async function updateOffering(id: string, input: Partial): Promise { + return saveOffering(`/api/offerings/${encodeURIComponent(id)}`, "PUT", input); +} + +async function saveOffering( + url: string, + method: "POST" | "PUT", + input: Partial, +): Promise { const normalized = normalizeOfferingInput(input); - const response = await fetch("/api/offerings", { - method: "POST", + const response = await fetch(url, { + method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(normalized), }); if (!response.ok) { - const payload = (await response.json().catch(() => null)) as OfferingErrorResponse | null; - - const message = payload?.error?.message ?? "The offering could not be saved."; - const fields = payload?.error?.fields ?? {}; + const { message, fields } = await readOfferingError(response, "The offering could not be saved."); throw new OfferingApiError(message, fields); } - return (await response.json()) as Offering; + const offering: unknown = await response.json(); + if (!isOffering(offering)) + throw new OfferingApiError("The server returned an unreadable save result. Reload the list before trying again."); + return offering; +} + +export async function deleteOffering(id: string): Promise { + const response = await fetch(`/api/offerings/${encodeURIComponent(id)}`, { method: "DELETE" }); + if (!response.ok) { + throw new OfferingApiError((await readOfferingError(response, "The offering could not be deleted.")).message); + } } diff --git a/src/proxy.ts b/src/proxy.ts new file mode 100644 index 0000000..8f16fb6 --- /dev/null +++ b/src/proxy.ts @@ -0,0 +1,8 @@ +import { NextResponse, type NextRequest } from "next/server"; +import { requireManagementAccess } from "@/server/management-access"; + +export async function proxy(request: NextRequest) { + return (await requireManagementAccess(request, false)) ?? NextResponse.next(); +} + +export const config = { matcher: ["/manage-offerings/:path*"] }; diff --git a/src/server/management-access.ts b/src/server/management-access.ts new file mode 100644 index 0000000..4e6987f --- /dev/null +++ b/src/server/management-access.ts @@ -0,0 +1,50 @@ +import { toErrorEnvelope } from "@/lib/offerings"; + +/** Shared by Node route handlers, the server page, and proxy. Credentials never enter client code. */ +export async function hasManagementAccess(authorization: string | null): Promise { + const username = process.env.MANAGEMENT_USERNAME; + const password = process.env.MANAGEMENT_PASSWORD; + if ( + !username || + username.includes(":") || + !password || + password.length < 16 || + /[{}<>]|CHANGE_ME|replace-with/i.test(password) || + !authorization + ) + return false; + const encode = new TextEncoder(); + const expected = `Basic ${btoa(String.fromCharCode(...encode.encode(`${username}:${password}`)))}`; + const [actualHash, expectedHash] = await Promise.all([ + crypto.subtle.digest("SHA-256", encode.encode(authorization)), + crypto.subtle.digest("SHA-256", encode.encode(expected)), + ]); + const actual = new Uint8Array(actualHash); + const expectedBytes = new Uint8Array(expectedHash); + let difference = 0; + for (let index = 0; index < actual.length; index++) difference |= actual[index] ^ expectedBytes[index]; + return difference === 0; +} + +export async function requireManagementAccess(request: Request, checkOrigin = true): Promise { + if (!(await hasManagementAccess(request.headers.get("authorization")))) { + return Response.json(toErrorEnvelope("UNAUTHORIZED", "Team sign-in is required to manage offerings."), { + status: 401, + headers: { + "WWW-Authenticate": 'Basic realm="Coffee Shop management", charset="UTF-8"', + "Cache-Control": "no-store", + }, + }); + } + const origin = request.headers.get("origin"); + const requestUrl = new URL(request.url); + // Next.js can normalize the internal URL hostname. Host retains the browser's target. + const expectedOrigin = `${requestUrl.protocol}//${request.headers.get("host") ?? requestUrl.host}`; + if (checkOrigin && origin && origin !== expectedOrigin) { + return Response.json(toErrorEnvelope("FORBIDDEN", "Use the shop's management page to make changes."), { + status: 403, + headers: { "Cache-Control": "no-store" }, + }); + } + return null; +} diff --git a/src/server/offering-handlers.ts b/src/server/offering-handlers.ts new file mode 100644 index 0000000..520464e --- /dev/null +++ b/src/server/offering-handlers.ts @@ -0,0 +1,90 @@ +import type { CreateOfferingInput, Offering } from "@/types/offering"; +import { requireManagementAccess } from "@/server/management-access"; +import { normalizeOfferingInput, toErrorEnvelope, validateOfferingInput } from "@/lib/offerings"; +import { OFFERING_CATEGORIES, type OfferingErrorCode } from "@/types/offering"; + +export interface OfferingStore { + list(): Promise; + create(input: CreateOfferingInput): Promise; + update(id: string, input: CreateOfferingInput): Promise; + remove(id: string): Promise; +} + +const reply = (body: unknown, status = 200) => + Response.json(body, { status, headers: { "Cache-Control": "no-store" } }); +const failure = (code: OfferingErrorCode, message: string, status: number, fields?: Record) => + reply(toErrorEnvelope(code, message, fields), status); + +async function readInput(request: Request): Promise { + if (request.headers.get("content-type")?.split(";")[0].trim().toLowerCase() !== "application/json") { + return failure("INVALID_CONTENT_TYPE", "Send offering details as JSON.", 415); + } + let body: unknown; + try { + body = await request.json(); + } catch { + return failure("INVALID_JSON", "The request contains invalid JSON.", 400); + } + if (!body || typeof body !== "object" || Array.isArray(body)) { + return failure("INVALID_INPUT", "Send one offering object.", 400); + } + const values = body as Record; + const fields: Record = {}; + if (Object.keys(values).some((key) => !["name", "description", "price", "category", "specialOffer"].includes(key))) { + return failure("INVALID_INPUT", "Only offering detail fields are accepted.", 400); + } + if (typeof values.price !== "number") fields.price = "Price must be a JSON number."; + if (!OFFERING_CATEGORIES.some((category) => category === values.category)) + fields.category = "Choose a valid category."; + Object.assign(fields, validateOfferingInput(values)); + if (Object.keys(fields).length) return failure("INVALID_INPUT", "Please correct the offering details.", 400, fields); + return normalizeOfferingInput(values); +} + +export function createOfferingHandlers(store: OfferingStore) { + return { + async GET() { + try { + return reply(await store.list()); + } catch { + return failure("DATABASE_UNAVAILABLE", "Offerings are temporarily unavailable. Please try again.", 503); + } + }, + async POST(request: Request) { + const denied = await requireManagementAccess(request); + if (denied) return denied; + const input = await readInput(request); + if (input instanceof Response) return input; + try { + return reply(await store.create(input), 201); + } catch { + return failure("DATABASE_UNAVAILABLE", "The offering could not be saved. Please try again.", 503); + } + }, + async PUT(request: Request, id: string) { + const denied = await requireManagementAccess(request); + if (denied) return denied; + if (!/^[a-f\d]{24}$/i.test(id)) return failure("INVALID_INPUT", "Choose a valid offering.", 400); + const input = await readInput(request); + if (input instanceof Response) return input; + try { + const updated = await store.update(id, input); + return updated ? reply(updated) : failure("NOT_FOUND", "That offering no longer exists. Reload the list.", 404); + } catch { + return failure("DATABASE_UNAVAILABLE", "The offering could not be updated. Please try again.", 503); + } + }, + async DELETE(request: Request, id: string) { + const denied = await requireManagementAccess(request); + if (denied) return denied; + if (!/^[a-f\d]{24}$/i.test(id)) return failure("INVALID_INPUT", "Choose a valid offering.", 400); + try { + return (await store.remove(id)) + ? new Response(null, { status: 204, headers: { "Cache-Control": "no-store" } }) + : failure("NOT_FOUND", "That offering no longer exists. Reload the list.", 404); + } catch { + return failure("DATABASE_UNAVAILABLE", "The offering could not be deleted. Please try again.", 503); + } + }, + }; +} diff --git a/src/styles/manage-offerings.module.css b/src/styles/manage-offerings.module.css index d8f4fb5..9ce15f0 100644 --- a/src/styles/manage-offerings.module.css +++ b/src/styles/manage-offerings.module.css @@ -29,6 +29,7 @@ } .panel { + min-width: 0; background: #fffaf7; border: 1px solid #e9d9d0; border-radius: 18px; @@ -61,6 +62,7 @@ .select, .textarea { width: 100%; + box-sizing: border-box; border: 1px solid #caaea5; border-radius: 10px; padding: 0.75rem 0.8rem; @@ -101,12 +103,27 @@ } .offerItem { + overflow-wrap: anywhere; border: 1px solid #ecd8d0; border-radius: 12px; padding: 0.85rem 0.9rem; background: #fff; } +.actions { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.75rem; +} + +.confirmation { + border: 1px solid #a3332d; + border-radius: 10px; + padding: 0.75rem; + margin-top: 0.75rem; +} + .offerItem h3 { margin: 0 0 0.2rem; font-size: 1.1rem; diff --git a/src/styles/offerings.module.css b/src/styles/offerings.module.css index cb6a427..b665c7a 100644 --- a/src/styles/offerings.module.css +++ b/src/styles/offerings.module.css @@ -44,6 +44,35 @@ border-top: 1px solid #ded8d0; } +.item > div { + min-width: 0; + overflow-wrap: anywhere; +} + +.retry { + padding: 0.75rem 1rem; + border: 1px solid #7d4d3e; + border-radius: 8px; + background: #fffaf7; + color: #2f241f; + font: inherit; + cursor: pointer; +} + +.retry:focus-visible { + outline: 3px solid #7d4d3e; + outline-offset: 3px; +} + +@media (max-width: 420px) { + .page { + padding: 2rem 1rem; + } + .item { + gap: 0.75rem; + } +} + .item h2 { margin: 0 0 0.4rem; font-size: 1.25rem; diff --git a/src/types/offering.ts b/src/types/offering.ts index 222a532..27e5adf 100644 --- a/src/types/offering.ts +++ b/src/types/offering.ts @@ -26,7 +26,14 @@ export type CreateOfferingInput = Omit & { specialOffer?: boolean; }; -export type OfferingErrorCode = "INVALID_CONTENT_TYPE" | "INVALID_JSON" | "INVALID_INPUT" | "DATABASE_UNAVAILABLE"; +export type OfferingErrorCode = + | "INVALID_CONTENT_TYPE" + | "INVALID_JSON" + | "INVALID_INPUT" + | "DATABASE_UNAVAILABLE" + | "UNAUTHORIZED" + | "FORBIDDEN" + | "NOT_FOUND"; export interface OfferingErrorResponse { error: { diff --git a/tests/helpers/http-server.mjs b/tests/helpers/http-server.mjs index 96a1b67..fdc516b 100644 --- a/tests/helpers/http-server.mjs +++ b/tests/helpers/http-server.mjs @@ -4,48 +4,92 @@ import { createServer } from "node:net"; import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; import { setTimeout as delay } from "node:timers/promises"; +import { MongoMemoryServer } from "mongodb-memory-server-core"; const require = createRequire(import.meta.url); const projectRoot = fileURLToPath(new URL("../../", import.meta.url)); /** Start the real HTTP interface with separate output and no Atlas credentials. */ -export async function startHttpServer() { +export async function startHttpServer({ seed = true } = {}) { + const database = await MongoMemoryServer.create({ + binary: { version: "7.0.24" }, + instance: { dbName: "coffee_http_test" }, + }); + const databaseUri = database.getUri("coffee_http_test"); + const environment = { + ...process.env, + MONGO_URI: databaseUri, + MANAGEMENT_USERNAME: "team", + MANAGEMENT_PASSWORD: "test-only-password", + __NEXT_PROCESSED_ENV: "", + }; + try { + if (seed) { + const seeder = spawn(process.execPath, ["--import", "tsx", "scripts/seed-offerings.ts", "--apply"], { + cwd: projectRoot, + env: environment, + windowsHide: true, + timeout: 30000, + stdio: ["ignore", "pipe", "pipe"], + }); + let diagnostics = ""; + for (const stream of [seeder.stdout, seeder.stderr]) + stream.on("data", (data) => { + diagnostics += data.toString(); + }); + const [exitCode] = await once(seeder, "close"); + if (exitCode !== 0) throw new Error(`Disposable seed failed. ${diagnostics}`); + } + } catch (error) { + await database.stop(); + throw error; + } const portReservation = createServer(); portReservation.listen(0, "127.0.0.1"); await once(portReservation, "listening"); const { port } = portReservation.address(); await new Promise((resolve, reject) => portReservation.close((error) => (error ? reject(error) : resolve()))); - const server = spawn( - process.execPath, - [require.resolve("next/dist/bin/next"), "dev", "--hostname", "127.0.0.1", "--port", String(port)], - { - cwd: projectRoot, - env: { - ...process.env, - NODE_ENV: "development", - MONGO_URI: "", - COFFEE_SHOP_HTTP_TEST: "1", - NEXT_TELEMETRY_DISABLED: "1", - }, - windowsHide: true, - detached: process.platform !== "win32", - stdio: ["ignore", "pipe", "pipe"], - }, - ); - + let server; let output = ""; let startupError; - server.on("error", (error) => { - startupError = error; - }); - for (const stream of [server.stdout, server.stderr]) { - stream.on("data", (chunk) => { - output = (output + chunk.toString()).slice(-4000); + function launch() { + startupError = undefined; + server = spawn( + process.execPath, + [require.resolve("next/dist/bin/next"), "dev", "--hostname", "127.0.0.1", "--port", String(port)], + { + cwd: projectRoot, + env: { + ...environment, + NODE_ENV: "development", + COFFEE_SHOP_HTTP_TEST: "1", + NEXT_TELEMETRY_DISABLED: "1", + }, + windowsHide: true, + detached: process.platform !== "win32", + stdio: ["ignore", "pipe", "pipe"], + }, + ); + server.on("error", (error) => { + startupError = error; }); + for (const stream of [server.stdout, server.stderr]) { + stream.on("data", (chunk) => { + output = (output + chunk.toString()).slice(-4000); + }); + } } async function stop() { + try { + await stopApp(); + } finally { + await database.stop(); + } + } + + async function stopApp() { if (server.exitCode !== null || server.signalCode !== null || !server.pid) return; const exited = once(server, "exit"); if (process.platform === "win32") { @@ -62,8 +106,8 @@ export async function startHttpServer() { } const baseUrl = `http://127.0.0.1:${port}`; - const deadline = Date.now() + 90_000; - try { + async function ready() { + const deadline = Date.now() + 90_000; while (Date.now() < deadline) { if (startupError) throw startupError; if (server.exitCode !== null || server.signalCode !== null) { @@ -72,13 +116,29 @@ export async function startHttpServer() { try { const response = await fetch(`${baseUrl}/api/offerings`, { signal: AbortSignal.timeout(3000) }); await response.arrayBuffer(); - if (response.ok) return { baseUrl, stop }; + if (response.ok) return; } catch { // The listener and first route compilation may still be starting. } await delay(250); } throw new Error(`HTTP test server did not become ready within 90 seconds.\n${output}`); + } + try { + launch(); + await ready(); + return { + baseUrl, + stop, + stopDatabase: () => database.stop(), + diagnostics: () => output, + authorization: `Basic ${Buffer.from("team:test-only-password").toString("base64")}`, + async restart() { + await stopApp(); + launch(); + await ready(); + }, + }; } catch (error) { await stop(); throw error; diff --git a/tests/offering-form.test.mjs b/tests/offering-form.test.mjs index 1d071c5..3e4f8c1 100644 --- a/tests/offering-form.test.mjs +++ b/tests/offering-form.test.mjs @@ -18,7 +18,7 @@ require.extensions[".css"] = (module) => { globalThis.React = React; const { createRoot } = await import("react-dom/client"); const OfferingForm = require("../src/components/OfferingForm.tsx").default; -const ManageOfferingsPage = require("../src/app/manage-offerings/page.tsx").default; +const ManageOfferingsPage = require("../src/components/ManageOfferings.tsx").default; const { submitOffering } = require("../src/lib/offerings.ts"); const values = { name: "Latte", description: "Espresso and milk", price: "4.25", category: "Coffee" }; @@ -114,7 +114,7 @@ test("pending saves lock every control, prevent duplicates, retain failures, and Object.getOwnPropertyDescriptor(prototype, "value").set.call(element, value); element.dispatchEvent(new window.Event("input", { bubbles: true })); } - field("special-offer").click(); + mounted.container.querySelector('input[type="checkbox"]').click(); }); const form = mounted.container.querySelector("form"); await act(async () => form.dispatchEvent(new window.Event("submit", { bubbles: true, cancelable: true }))); @@ -139,7 +139,7 @@ test("pending saves lock every control, prevent duplicates, retain failures, and assert.equal(field("offering-name").value, succeeds ? "" : "Latte"); assert.equal(field("offering-description").value, succeeds ? "" : "Espresso and milk"); assert.equal(field("offering-price").value, succeeds ? "" : "4.25"); - assert.equal(field("special-offer").checked, !succeeds); + assert.equal(mounted.container.querySelector('input[type="checkbox"]').checked, !succeeds); assert.equal(field("offering-name").matches(":disabled"), false); assert.match( mounted.container.textContent, diff --git a/tests/offerings-http.test.mjs b/tests/offerings-http.test.mjs index 3b45cc3..272ed01 100644 --- a/tests/offerings-http.test.mjs +++ b/tests/offerings-http.test.mjs @@ -3,6 +3,7 @@ import { createRequire } from "node:module"; import { after, afterEach, before, describe, it, mock, test } from "node:test"; import { JSDOM } from "jsdom"; import { startHttpServer } from "./helpers/http-server.mjs"; +import { verifyLive } from "../scripts/live-acceptance.ts"; let server; before( @@ -37,22 +38,22 @@ test("GET preserves the starting menu and public offering fields", async () => { assert.deepEqual( offerings.map((offering) => offering.name), [ + "Cafe au Lait", "Cappuccino", - "Pumpkin Spice Latte", - "Golden Hour Cold Brew", - "Mocha", - "Espresso", - "Latte", - "Cortado", - "English Breakfast Tea", - "Matcha", "Chai Latte", "Chamomile Tea", - "Cafe au Lait", - "London Fog", - "Iced Hibiscus Berry", + "Cortado", + "English Breakfast Tea", + "Espresso", "Fruit Smoothie", + "Golden Hour Cold Brew", "Hojicha Latte", + "Iced Hibiscus Berry", + "Latte", + "London Fog", + "Matcha", + "Mocha", + "Pumpkin Spice Latte", ], ); for (const offering of offerings) { @@ -61,7 +62,7 @@ test("GET preserves the starting menu and public offering fields", async () => { assert.equal(typeof offering.price, "number"); assert.equal(typeof offering.specialOffer, "boolean"); } - const { ID, ...cappuccino } = offerings[0]; + const { ID, ...cappuccino } = offerings.find((offering) => offering.name === "Cappuccino"); assert.deepEqual(cappuccino, { name: "Cappuccino", description: "A delicious cappuccino with steamed milk and foam", @@ -76,8 +77,88 @@ test("GET flags exactly the three starting special offerings", async () => { const offerings = await response.json(); assert.deepEqual( offerings.filter((offering) => offering.specialOffer).map((offering) => offering.name), - ["Pumpkin Spice Latte", "Golden Hour Cold Brew", "Hojicha Latte"], + ["Golden Hour Cold Brew", "Hojicha Latte", "Pumpkin Spice Latte"], + ); +}); + +test("real HTTP writes require sign-in and persist create, edit, and delete", async () => { + const url = `${server.baseUrl}/api/offerings`; + const input = { + name: "HTTP test latte", + description: "Disposable test offering", + price: 4.25, + category: "Coffee", + specialOffer: true, + }; + const headers = { Authorization: server.authorization, "Content-Type": "application/json", Origin: server.baseUrl }; + assert.equal( + (await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(input) })) + .status, + 401, + ); + assert.equal((await fetch(`${server.baseUrl}/manage-offerings`)).status, 401); + assert.equal((await fetch(`${server.baseUrl}/manage-offerings`, { headers })).status, 200); + assert.equal( + ( + await fetch(url, { + method: "POST", + headers: { ...headers, Origin: "https://attacker.example" }, + body: JSON.stringify(input), + }) + ).status, + 403, ); + assert.equal( + (await fetch(url, { method: "POST", headers, body: JSON.stringify({ ...input, price: "4.25" }) })).status, + 400, + ); + const created = await fetch(url, { method: "POST", headers, body: JSON.stringify(input) }); + assert.equal(created.status, 201); + const offering = await created.json(); + assert.match(offering.ID, /^[a-f\d]{24}$/); + await server.restart(); + assert.deepEqual( + (await (await fetch(url)).json()).find((item) => item.ID === offering.ID), + offering, + ); + assert.equal( + ( + await fetch(`${url}/${offering.ID}`, { + method: "PUT", + headers, + body: JSON.stringify({ ...input, name: "Edited latte", specialOffer: false }), + }) + ).status, + 200, + ); + assert.equal((await (await fetch(url)).json()).find((item) => item.ID === offering.ID).name, "Edited latte"); + assert.equal((await fetch(`${url}/${offering.ID}`, { method: "DELETE", headers })).status, 204); + assert.equal( + (await (await fetch(url)).json()).some((item) => item.ID === offering.ID), + false, + ); + assert.equal((await fetch(`${url}/${offering.ID}`, { method: "DELETE", headers })).status, 404); +}); + +test("live acceptance tooling verifies all operations and removes only its own marker", async () => { + const before = await (await fetch(`${server.baseUrl}/api/offerings`)).json(); + const result = await verifyLive({ + baseUrl: server.baseUrl, + username: "team", + password: "test-only-password", + allowWrites: true, + allOperations: true, + }); + assert.equal(result.writes, true); + assert.equal(result.updated, true); + assert.deepEqual(await (await fetch(`${server.baseUrl}/api/offerings`)).json(), before); +}); + +test("an unavailable database returns sanitized 503 without a fixture fallback", async () => { + await server.stopDatabase(); + const response = await fetch(`${server.baseUrl}/api/offerings`); + assert.equal(response.status, 503); + assert.match((await response.json()).error.message, /temporarily unavailable/); }); // Test-only fixtures. Names, categories, and flags mirror the starting menu. diff --git a/tests/seed-offerings.test.mjs b/tests/seed-offerings.test.mjs new file mode 100644 index 0000000..6261ca8 --- /dev/null +++ b/tests/seed-offerings.test.mjs @@ -0,0 +1,75 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { once } from "node:events"; +import test from "node:test"; +import { MongoMemoryServer } from "mongodb-memory-server-core"; +import mongoose from "mongoose"; + +test("insert-only seeding preserves offering edits, IDs, and unrelated records", { timeout: 60000 }, async (t) => { + const database = await MongoMemoryServer.create({ binary: { version: "7.0.24" } }); + const uri = database.getUri("coffee_seed_test"); + process.env.MONGO_URI = uri; + const { offeringStore } = await import("../src/database/offeringStore.ts"); + async function seed(args = ["--apply"]) { + const child = spawn(process.execPath, ["--import", "tsx", "scripts/seed-offerings.ts", ...args], { + env: { ...process.env, MONGO_URI: uri }, + windowsHide: true, + stdio: ["ignore", "pipe", "pipe"], + }); + let output = ""; + for (const stream of [child.stdout, child.stderr]) + stream.on("data", (data) => { + output += data.toString(); + }); + const [code] = await once(child, "close"); + return { code, output }; + } + try { + await t.test("first seed inserts 16 public offerings with exactly three specials", async () => { + assert.equal((await seed()).code, 0); + const offerings = await offeringStore.list(); + assert.equal(offerings.length, 16); + assert.equal(offerings.filter((offering) => offering.specialOffer).length, 3); + for (const offering of offerings) { + assert.match(offering.ID, /^[a-f\d]{24}$/); + assert.deepEqual(Object.keys(offering).sort(), [ + "ID", + "category", + "description", + "name", + "price", + "specialOffer", + ]); + } + }); + await t.test("rerun preserves edited seeded records and unrelated creations", async () => { + const cappuccino = (await offeringStore.list()).find((offering) => offering.name === "Cappuccino"); + const { ID, ...details } = cappuccino; + await offeringStore.update(ID, { ...details, price: 8.25 }); + const unrelated = await offeringStore.create({ + name: "Team creation", + description: "Keep this offering", + price: 3, + category: "Tea", + }); + assert.equal((await seed()).code, 0); + const offerings = await offeringStore.list(); + assert.equal(offerings.length, 17); + assert.equal(offerings.find((offering) => offering.ID === ID).price, 8.25); + assert.deepEqual( + offerings.find((offering) => offering.ID === unrelated.ID), + { ...unrelated, specialOffer: false }, + ); + }); + await t.test("seed refuses execution without explicit confirmation", async () => { + const before = await offeringStore.list(); + const result = await seed([]); + assert.equal(result.code, 1); + assert.doesNotMatch(result.output, /mongodb:\/\//); + assert.deepEqual(await offeringStore.list(), before); + }); + } finally { + await mongoose.disconnect(); + await database.stop(); + } +}); diff --git a/tests/unit/README.md b/tests/unit/README.md index e06cdfc..0884a9c 100644 --- a/tests/unit/README.md +++ b/tests/unit/README.md @@ -10,7 +10,7 @@ Start from updated `main`. You can complete this customer-page assignment withou waiting for PR #31. Extend `customer-pages.test.tsx`, which renders the shared `OfferingsPage` component used by `/menu` and `/specials`. -The file contains one foundation loading test, browser setup, and cleanup. Vitest +The file contains five reference tests, browser setup, and cleanup. Vitest restores stubbed globals after each test. Optional sample data and a JSON response helper are in `helpers/offerings.ts`. None of your assigned Vitest cases has been implemented for you. Existing Node regressions cover related UI states; keep them @@ -46,14 +46,12 @@ Controlled responses verify UI behavior; they do not prove live database persist ## Other developer assignments and release integration -This foundation does not copy the release branch's completed unit suites into -`main`. Its handler, replacement management component, and live acceptance runner -are release-only modules until integrated. Their tests stay with PR #31, and their -owners should follow their live issues for when to start. Compatible baseline -coverage on current `main` is the loading example above; all existing Node -regressions remain enabled. +PR #31 integrates the handlers, replacement management component, live acceptance +runner, and their reference tests into `main`. After it merges, developers can +start from updated `main` and follow their assigned issues. Sofie's issue #36 +extends `scripts/live-acceptance.ts` through controlled fetch responses. Deployment +and live verification remain separate work, tracked in issue #30. -When reconciling PR #31, preserve this handoff and each developer's contributed -cases. Keep agent-written reference tests distinct from personal contributions. -Reconcile the Vitest version/configuration with the release runtime, and retain +Preserve this handoff and each developer's contributed cases. Keep agent-written +reference tests distinct from personal contributions. Retain both unit and regression commands, including storage and seed coverage from `main`. diff --git a/tests/unit/customer-pages.test.tsx b/tests/unit/customer-pages.test.tsx index 3fb8938..2bd5090 100644 --- a/tests/unit/customer-pages.test.tsx +++ b/tests/unit/customer-pages.test.tsx @@ -1,21 +1,69 @@ // @vitest-environment jsdom +import React from "react"; import { afterEach, expect, it, vi } from "vitest"; -import { cleanup, render, screen } from "@testing-library/react"; +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import OfferingsPage from "@/components/OfferingsPage"; afterEach(cleanup); - const props = { title: "Menu", intro: "Our drinks", emptyMessage: "No offerings yet." }; +const item = { + ID: "saved", + name: "Latte", + description: "Espresso and milk", + price: 4.25, + category: "Coffee", + specialOffer: false, +}; +const response = (body: unknown, status = 200) => new Response(JSON.stringify(body), { status }); -// Foundation example only. This is not one of Vedika's three contributions. +// Agent-written foundation/reference coverage, not a personal contribution. it("announces loading while a request is pending", () => { vi.stubGlobal( "fetch", - vi.fn(() => new Promise(() => {})), + vi.fn(() => new Promise(() => {})), ); render(); expect(screen.getByRole("status").textContent).toBe("Loading menu..."); }); -// Issue #35: write three NEW cases below. See ./README.md for the requirements. -// Leave the example above in place; do not count it as a personal contribution. +it("filters Specials by the saved boolean flag", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue(response([item, { ...item, ID: "special", name: "Seasonal latte", specialOffer: true }])), + ); + render(); + await screen.findByRole("heading", { name: "Seasonal latte" }); + expect(screen.queryByRole("heading", { name: "Latte" })).toBeNull(); +}); + +it("treats malformed success data as an error rather than crashing the page", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(response([{ ...item, price: "4.25" }]))); + render(); + expect((await screen.findByRole("alert")).textContent).toBe("Unable to load menu."); +}); + +it("shows an empty state for an empty successful response", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(response([]))); + render(); + await screen.findByText("No offerings yet."); + expect(screen.queryByRole("alert")).toBeNull(); +}); + +it("shows a readable failure and can retry successfully", async () => { + vi.stubGlobal( + "fetch", + vi + .fn() + .mockResolvedValueOnce(response({ error: { message: "Please try again." } }, 503)) + .mockResolvedValueOnce(response([item])), + ); + render(); + expect((await screen.findByRole("alert")).textContent).toBe("Please try again."); + expect(screen.queryByText("No offerings yet.")).toBeNull(); + fireEvent.click(screen.getByRole("button", { name: "Try again" })); + await screen.findByRole("heading", { name: "Latte" }); + expect(screen.queryByRole("alert")).toBeNull(); +}); + +// Issue #35: write three NEW cases. See ./README.md for the assigned scenarios. +// Keep the reference tests above; do not count them as personal contributions. diff --git a/tests/unit/live-acceptance.test.ts b/tests/unit/live-acceptance.test.ts new file mode 100644 index 0000000..d6f3fc3 --- /dev/null +++ b/tests/unit/live-acceptance.test.ts @@ -0,0 +1,43 @@ +import { expect, it, vi } from "vitest"; +import { verifyLive } from "../../scripts/live-acceptance"; + +it("fails acceptance for an incorrect GET status", async () => { + const fetcher = vi.fn().mockResolvedValue(new Response("unavailable", { status: 503 })); + await expect(verifyLive({ baseUrl: "https://shop.example", fetcher })).rejects.toThrow(/GET/); +}); + +it("fails acceptance for a malformed public offering", async () => { + const fetcher = vi.fn().mockResolvedValue(new Response(JSON.stringify([{ ID: "broken", price: "4.25" }]))); + await expect(verifyLive({ baseUrl: "https://shop.example", fetcher })).rejects.toThrow(/invalid public offering/); +}); + +it("fails when an acknowledged creation is missing from subsequent GET", async () => { + const fetcher = vi.fn(async (_url: unknown, init?: RequestInit) => { + if (init?.method === "POST" && !(init.headers as Record)?.Authorization) + return new Response(null, { status: 401 }); + if (init?.method === "POST") + return new Response(JSON.stringify({ ID: "new-id", ...JSON.parse(String(init.body)) }), { status: 201 }); + return new Response("[]"); + }); + await expect( + verifyLive({ + baseUrl: "https://shop.example", + fetcher, + allowWrites: true, + username: "team", + password: "test-only-password", + }), + ).rejects.toThrow(/missing or changed/); +}); + +it("refuses credential-bearing URLs and remote plain HTTP before making requests", async () => { + const fetcher = vi.fn(); + for (const baseUrl of [ + "http://shop.example", + "https://team:password@shop.example", + "https://shop.example/?token=secret", + ]) { + await expect(verifyLive({ baseUrl, fetcher })).rejects.toThrow(/HTTPS origin/); + } + expect(fetcher).not.toHaveBeenCalled(); +}); diff --git a/tests/unit/management.test.tsx b/tests/unit/management.test.tsx new file mode 100644 index 0000000..abed81f --- /dev/null +++ b/tests/unit/management.test.tsx @@ -0,0 +1,128 @@ +// @vitest-environment jsdom +import React from "react"; +import { afterEach, expect, it, vi } from "vitest"; +import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import ManageOfferings from "@/components/ManageOfferings"; + +const offering = { + ID: "0123456789abcdef01234567", + name: "Latte", + description: "Espresso and milk", + price: 4.25, + category: "Coffee", + specialOffer: false, +}; +const response = (body: unknown, status = 200) => new Response(JSON.stringify(body), { status }); +afterEach(cleanup); + +it("keeps the form usable when a failed API returns malformed display fields", async () => { + const fetcher = vi + .fn() + .mockResolvedValueOnce(response([])) + .mockResolvedValueOnce( + response( + { error: { message: {}, fields: { name: { invalid: true }, description: "Try a shorter description." } } }, + 503, + ), + ); + vi.stubGlobal("fetch", fetcher); + render(); + await screen.findByText("No offerings are available right now."); + fireEvent.change(screen.getByLabelText("Name"), { target: { value: "New latte" } }); + fireEvent.change(screen.getByLabelText("Description"), { target: { value: "Oat milk" } }); + fireEvent.change(screen.getByLabelText("Price"), { target: { value: "4.25" } }); + fireEvent.click(screen.getByRole("button", { name: "Save offering" })); + await screen.findByText("The offering could not be saved."); + expect(screen.getByText("Try a shorter description.")).toBeTruthy(); + expect((screen.getByLabelText("Name") as HTMLInputElement).value).toBe("New latte"); +}); + +it("edits the selected offering and sends its persisted identity", async () => { + const fetcher = vi + .fn() + .mockResolvedValueOnce(response([offering])) + .mockResolvedValueOnce(response({ ...offering, name: "Oat latte" })) + .mockResolvedValueOnce(response([{ ...offering, name: "Oat latte" }])); + vi.stubGlobal("fetch", fetcher); + render(); + fireEvent.click(await screen.findByRole("button", { name: "Edit Latte" })); + const name = screen.getByLabelText("Name"); + expect((name as HTMLInputElement).value).toBe("Latte"); + fireEvent.change(name, { target: { value: "Oat latte" } }); + fireEvent.click(screen.getByRole("button", { name: "Save changes" })); + await screen.findByText("Offering updated successfully."); + expect(fetcher.mock.calls[1][0]).toBe(`/api/offerings/${offering.ID}`); + expect(fetcher.mock.calls[1][1].method).toBe("PUT"); + await waitFor(() => expect(screen.queryByRole("button", { name: "Edit Latte" })).toBeNull()); +}); + +it("requires confirmation before deleting an offering", async () => { + const fetcher = vi + .fn() + .mockResolvedValueOnce(response([offering])) + .mockResolvedValueOnce(new Response(null, { status: 204 })) + .mockResolvedValueOnce(response([])); + vi.stubGlobal("fetch", fetcher); + render(); + fireEvent.click(await screen.findByRole("button", { name: "Delete Latte" })); + expect(fetcher).toHaveBeenCalledTimes(1); + expect(screen.getByRole("group", { name: "Confirm deletion of Latte" })).toBeTruthy(); + fireEvent.click(screen.getByRole("button", { name: "Confirm delete" })); + await screen.findByText("Offering deleted successfully."); + await screen.findByText("No offerings are available right now."); + expect(fetcher.mock.calls[1][1].method).toBe("DELETE"); +}); + +it("locks all inputs during saving and retains values after failure", async () => { + let finishSave!: (value: Response) => void; + const fetcher = vi + .fn() + .mockResolvedValueOnce(response([])) + .mockImplementationOnce( + () => + new Promise((resolve) => { + finishSave = resolve; + }), + ); + vi.stubGlobal("fetch", fetcher); + render(); + await screen.findByText("No offerings are available right now."); + fireEvent.change(screen.getByLabelText("Name"), { target: { value: "New latte" } }); + fireEvent.change(screen.getByLabelText("Description"), { target: { value: "Oat milk" } }); + fireEvent.change(screen.getByLabelText("Price"), { target: { value: "4.25" } }); + fireEvent.click(screen.getByRole("checkbox", { name: "Special offering" })); + fireEvent.click(screen.getByRole("button", { name: "Save offering" })); + expect((screen.getByLabelText("Name") as HTMLInputElement).disabled).toBe(true); + expect((screen.getByRole("checkbox") as HTMLInputElement).disabled).toBe(true); + finishSave(response({ error: { message: "Saving is unavailable." } }, 503)); + await screen.findByText("Saving is unavailable."); + expect((screen.getByLabelText("Name") as HTMLInputElement).value).toBe("New latte"); + expect((screen.getByRole("checkbox") as HTMLInputElement).checked).toBe(true); + expect(screen.getByRole("button", { name: "Save offering" }).hasAttribute("disabled")).toBe(false); +}); + +it("reports successful saving separately from a failed list reload", async () => { + vi.stubGlobal( + "fetch", + vi + .fn() + .mockResolvedValueOnce(response([offering])) + .mockResolvedValueOnce(response(offering, 201)) + .mockResolvedValueOnce(response({ error: { message: "Reload failed." } }, 503)), + ); + render(); + await screen.findByRole("button", { name: "Edit Latte" }); + for (const [label, value] of [ + ["Name", "Latte"], + ["Description", "Espresso and milk"], + ["Price", "4.25"], + ]) { + fireEvent.change(screen.getByLabelText(label), { target: { value } }); + } + fireEvent.click(screen.getByRole("button", { name: "Save offering" })); + await screen.findByText( + "Offering saved, but the list could not refresh. Please reload the page to confirm the update.", + ); + expect((screen.getByLabelText("Name") as HTMLInputElement).value).toBe(""); + expect(screen.getByRole("button", { name: "Retry loading offerings" })).toBeTruthy(); +}); diff --git a/tests/unit/offering-handlers.test.ts b/tests/unit/offering-handlers.test.ts new file mode 100644 index 0000000..ac18a0d --- /dev/null +++ b/tests/unit/offering-handlers.test.ts @@ -0,0 +1,128 @@ +import { describe, expect, it, vi } from "vitest"; +import { createOfferingHandlers } from "@/server/offering-handlers"; +import type { Offering } from "@/types/offering"; + +const input = { name: "Latte", description: "Espresso and milk", price: 4.25, category: "Coffee" }; +const saved: Offering = { ...input, category: "Coffee", ID: "0123456789abcdef01234567", specialOffer: false }; +const authorization = `Basic ${Buffer.from("team:test-only-password").toString("base64")}`; + +function setup() { + vi.stubEnv("MANAGEMENT_USERNAME", "team"); + vi.stubEnv("MANAGEMENT_PASSWORD", "test-only-password"); + const records: Offering[] = []; + const store = { + async list() { + return [...records]; + }, + async create() { + records.push(saved); + return saved; + }, + async update() { + return null; + }, + async remove() { + return false; + }, + }; + return { handlers: createOfferingHandlers(store), store }; +} + +function request(body: unknown = input, headers: Record = {}) { + return new Request("https://shop.example/api/offerings", { + method: "POST", + headers: { "Content-Type": "application/json", Authorization: authorization, ...headers }, + body: JSON.stringify(body), + }); +} + +describe("offering HTTP handlers", () => { + it("accepts the browser Host when Next.js normalizes the internal hostname", async () => { + const { handlers } = setup(); + const response = await handlers.POST(request(input, { Host: "127.0.0.1:3000", Origin: "https://127.0.0.1:3000" })); + expect(response.status).toBe(201); + expect( + (await handlers.POST(request(input, { Host: "127.0.0.1:3000", Origin: "https://attacker.example" }))).status, + ).toBe(403); + }); + it("creates an offering and exposes the same public ID through GET", async () => { + const { handlers } = setup(); + const response = await handlers.POST(request()); + expect(response.status).toBe(201); + expect(await response.json()).toEqual(saved); + expect(await (await handlers.GET()).json()).toEqual([saved]); + }); + it.each( + [ + { ...input, price: "4.25" }, + { ...input, price: null }, + { ...input, name: " " }, + { ...input, price: -1 }, + { ...input, price: 4.251 }, + { ...input, category: " coffee " }, + { ...input, ID: "client-id" }, + { ...input, specialOffer: "false" }, + [], + null, + ].map((body) => ({ body })), + )("rejects invalid raw input without changing the list: %j", async ({ body }) => { + const { handlers } = setup(); + expect((await handlers.POST(request(body))).status).toBe(400); + expect(await (await handlers.GET()).json()).toEqual([]); + }); + it("sanitizes database failures on both reads and writes", async () => { + const { store } = setup(); + store.list = async () => { + throw new Error("mongodb://private-password@host"); + }; + store.create = async () => { + throw new Error("secret credentials"); + }; + const handlers = createOfferingHandlers(store); + for (const response of [await handlers.GET(), await handlers.POST(request())]) { + expect(response.status).toBe(503); + expect(await response.text()).not.toMatch(/private-password|secret credentials|mongodb/); + } + }); + it("denies missing credentials, missing configuration, and cross-origin writes", async () => { + const { handlers } = setup(); + expect((await handlers.POST(request(input, { Authorization: "" }))).status).toBe(401); + expect((await handlers.POST(request(input, { Origin: "https://attacker.example" }))).status).toBe(403); + vi.stubEnv("MANAGEMENT_PASSWORD", ""); + expect((await handlers.POST(request())).status).toBe(401); + expect(await (await handlers.GET()).json()).toEqual([]); + }); + it("reports invalid content type and malformed JSON", async () => { + const { handlers } = setup(); + expect((await handlers.POST(request(input, { "Content-Type": "text/plain" }))).status).toBe(415); + const malformed = new Request("https://shop.example/api/offerings", { + method: "POST", + headers: { Authorization: authorization, "Content-Type": "application/json" }, + body: "{", + }); + expect((await handlers.POST(malformed)).status).toBe(400); + }); + it("returns 404 for missing records and 400 for invalid identities", async () => { + const { handlers } = setup(); + expect((await handlers.PUT(request(), saved.ID)).status).toBe(404); + expect((await handlers.DELETE(request(), "bad-id")).status).toBe(400); + expect((await handlers.DELETE(request(), saved.ID)).status).toBe(404); + }); + it("enforces access and sanitizes database failures on update and delete", async () => { + const { store } = setup(); + store.update = async () => { + throw new Error("private database details"); + }; + store.remove = async () => { + throw new Error("private database details"); + }; + const handlers = createOfferingHandlers(store); + for (const operation of [handlers.PUT, handlers.DELETE]) { + expect((await operation(request(input, { Authorization: "" }), saved.ID)).status).toBe(401); + expect((await operation(request(input, { Origin: "https://attacker.example" }), saved.ID)).status).toBe(403); + const unavailable = await operation(request(), saved.ID); + expect(unavailable.status).toBe(503); + expect(await unavailable.text()).not.toContain("private database details"); + } + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 4b69b0a..dfa2400 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2017", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, @@ -11,7 +11,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -23,6 +23,14 @@ "@/*": ["*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next-http-test/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next-http-test/types/**/*.ts", + ".next-http-test/dev/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], "exclude": ["node_modules"] } diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..3294089 --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "nextjs", + "buildCommand": "npm run build", + "installCommand": "npm ci" +} diff --git a/vitest.config.mts b/vitest.config.mts index 0989b58..07ee8ad 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -4,7 +4,7 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) } }, // Next preserves JSX for its compiler; component tests need automatic JSX. - esbuild: { jsx: "automatic" }, + oxc: { jsx: { runtime: "automatic" } }, test: { include: ["tests/unit/**/*.test.{ts,tsx}"], environment: "node", From e06b7c9310afafee2b8443c6e960cb5f89f4a04f Mon Sep 17 00:00:00 2001 From: sofie-99 Date: Mon, 12 Oct 2026 23:22:56 -0700 Subject: [PATCH 29/29] test: complete standalone acceptance runner and demonstration checklist (#28) Complete the standalone loopback acceptance runner, controlled HTTP/CLI self-tests, owned disposable database lifecycle checks, and demonstration checklist with a pending six-person evidence matrix. Preserve the current application contract, authenticated writes, anonymous GET, and existing regressions. Validated with 118 tests, lint, typecheck, production build, independent Standards/Spec review, and passing PR CI. Live Atlas and team meeting evidence remains under #12. Closes #18 --- docs/issue-18-acceptance.md | 98 ++++++++++ docs/milestone-2-parallel-work.md | 2 +- package.json | 10 +- scripts/acceptance-runner.ts | 50 +++++ scripts/acceptance.ts | 271 +++++++++++++++++++++++++++ scripts/database-smoke.ts | 20 ++ scripts/disposable-database.ts | 72 +++++++ tests/acceptance-application.test.ts | 20 ++ tests/acceptance-cli.test.ts | 115 ++++++++++++ tests/acceptance-lifecycle.test.ts | 33 ++++ tests/controlled-server.ts | 112 +++++++++++ tests/fixtures/acceptance-seed.json | 10 + tests/unit/acceptance-runner.test.ts | 114 +++++++++++ 13 files changed, 924 insertions(+), 3 deletions(-) create mode 100644 docs/issue-18-acceptance.md create mode 100644 scripts/acceptance-runner.ts create mode 100644 scripts/acceptance.ts create mode 100644 scripts/database-smoke.ts create mode 100644 scripts/disposable-database.ts create mode 100644 tests/acceptance-application.test.ts create mode 100644 tests/acceptance-cli.test.ts create mode 100644 tests/acceptance-lifecycle.test.ts create mode 100644 tests/controlled-server.ts create mode 100644 tests/fixtures/acceptance-seed.json create mode 100644 tests/unit/acceptance-runner.test.ts diff --git a/docs/issue-18-acceptance.md b/docs/issue-18-acceptance.md new file mode 100644 index 0000000..4fd6672 --- /dev/null +++ b/docs/issue-18-acceptance.md @@ -0,0 +1,98 @@ +# Issue 18: acceptance runner and demonstration checklist + +Owner: Sofie. Assignment: [#18](https://github.com/BrianLiDesign/coffee-shop/issues/18). Brian owns actual integrated, Atlas, deployment, board, and meeting evidence under [#12](https://github.com/BrianLiDesign/coffee-shop/issues/12). + +This tooling verifies the public Offering HTTP contract independently. Controlled-server passes prove the runner; they do not prove application persistence. The separate application check uses real HTTP and an owned local database. Neither establishes shared Atlas or deployment readiness. + +## Commands and prerequisites + +Use the repository's Node 24 runtime and `npm ci`. + +| Command | Evidence supplied | +| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `npm run test:acceptance` | Runner self-tests over controlled HTTP, CLI success/failure exits, and independent disposable database lifecycle | +| `npm run acceptance:db` | Creates its own MongoDB, writes/reads, removes its own marker, preserves another record, closes/stops and removes its temporary database | +| `npm run test:acceptance:app` | Runs real application HTTP against an owned empty local database; verifies POST/read-back/cleanup and deliberately stops that database for 503 checks | +| `npm run acceptance -- --scenario empty` | Targets an explicitly configured isolated server whose collection must initially be empty | +| `npm run acceptance -- --scenario seeded` | Targets an explicitly configured isolated server and checks independently prepared seed expectations | +| `npm run acceptance -- --scenario database-failure` | Checks deliberately unavailable database GET and authorized POST, requiring sanitized 503 envelopes | +| `npm test` | Keeps current unit/regression checks and adds CLI, lifecycle, and real application acceptance checks | +| `npm run preflight` | Lint, typecheck, full tests, and production build | + +MongoDB 7.0.24 may download on first use. A download/start failure is a failed check, not a skip. The standalone lifecycle refuses any `MONGO_URI` or command-line arguments; it cannot drop a developer or integration database. It does not load `.env.local`. Clear inherited `MONGO_URI` in a dedicated terminal before using `acceptance:db`, or use `test:acceptance:db`, which owns its database regardless of inherited configuration. + +The lifecycle limits startup to 60 seconds (MongoDB launch itself to 20 seconds), individual operations and teardown to 15 seconds, and its CLI to 90 seconds overall. The HTTP CLI has 15-second requests and a 120-second overall deadline. A failed or timed-out cleanup is a failed run; inspect the disposable target before reusing it. Binary download interruption or an operating-system failure may leave local temporary files; never respond by deleting shared data. + +## Configured HTTP runner + +Only literal loopback origins such as `http://127.0.0.1:` or `http://[::1]:` are accepted. No remote hosts, `localhost` aliases, embedded credentials, paths, query strings, fragments, or redirects. The target must be dedicated disposable acceptance data, separate from developer/shared integration data. Loopback alone does not establish database isolation: verify the server's private database configuration first. + +In a dedicated PowerShell terminal, supply these environment values: + +```powershell +$env:ACCEPTANCE_BASE_URL = 'http://127.0.0.1:' +$env:ACCEPTANCE_CONFIRM_WRITES = 'disposable' +# Set MANAGEMENT_USERNAME and MANAGEMENT_PASSWORD privately to match this test server. +npm run acceptance -- --scenario empty +``` + +`ACCEPTANCE_CONFIRM_WRITES=disposable` acknowledges writes only to that isolated target. No default server is selected. The runner retains the current management authentication; every POST/DELETE carries those private credentials, while GET must work without authentication. It emits safe scenario/deadline information and named PASS/FAIL results, without printing URLs, paths, credentials, authorization, or raw server exceptions. Exit 0 means every selected scenario and cleanup passed; exit 1 means failure. + +For seeded mode, set `ACCEPTANCE_SEED_FILE` to a JSON array of complete expected public Offerings, in name-then-ID order. Prepare values and IDs from the independent seed setup record, not by copying the GET response being tested. A fixture example appears in `tests/fixtures/acceptance-seed.json`; its `seed-id` is only for controlled-server self-tests and is not a MongoDB identity. Missing expectations, changed details, extra records, wrong ordering, empty IDs, and duplicate IDs fail. + +Empty/seeded mode checks ordinary, special, and omitted-default creation; trimming, zero price, maximum text lengths, floating-point cents, categories, and persisted read-back. It rejects invalid shapes, field types, missing values, overlong text, negative/fractional-cent prices, nonfinite JSON numbers, categories, flags, unknown/public/internal identity fields, invalid content type, and malformed JSON. Error envelopes and field messages must follow the shared contract. Afterward it deletes only non-baseline records matching attempted create details, then verifies the original records remain unchanged. Use an exclusively owned target: concurrent changes intentionally fail baseline preservation checks. + +## Deliberate database failure setup + +Never select failure mode merely because a normal run failed. A 401, listener failure, timeout, or malformed response is not a sanitized database 503. + +Reproducible local setup: `npm run test:acceptance:app` first starts the real server with an owned disposable database and validates normal behavior. It then explicitly stops that database while keeping the HTTP server alive, runs the database-failure scenario, and tears down the app. Both GET and authenticated valid POST must return `503` with `DATABASE_UNAVAILABLE`, a readable message, and no connection strings, stack traces, raw database errors, or internal fields. + +For a separately configured local server, deliberately stop its owned disposable database while retaining valid application management credentials and its configured URI. Record that action, server commit, time, and isolated database alias, then run `--scenario database-failure`. Restore/recreate the disposable setup afterward and verify normal mode again. Never interrupt shared Atlas or another person's server for this check. + +Controlled `database-failure` responses in self-tests are labeled runner evidence only. The correct failure response passes only the explicitly selected failure scenario; it fails ordinary empty/seeded mode. Deliberately leaked database responses fail the failure scenario. + +## Manual browser demonstration checklist + +Record the commit, server/database alias, date, tester, outcome, and artifact for every checked item. Keep failures and pending evidence visible. + +- [ ] At desktop width and approximately 390px width, Menu, Specials, and management have readable content with no horizontal overflow. +- [ ] Use only keyboard navigation to reach fields, category selection, special checkbox, submit, and retry controls. Check visible focus, labels, and announced feedback. +- [ ] Starting Menu contains all independently seeded offerings; Specials contains only featured offerings. Confirm empty states in a separate empty setup. +- [ ] Create an ordinary offering, a special offering, and one with the default flag. Confirm the returned IDs and Menu/Specials filtering. +- [ ] Submit invalid/blank/overlong input and an invalid price. Confirm useful feedback, retained values, and no saved record. +- [ ] Save successfully, then deliberately fail the subsequent list reload in the controlled management preview. Confirm the save is acknowledged, the reload error is clear, and retry does not create a duplicate. Record this as controlled UI evidence, not Atlas persistence. +- [ ] Refresh the browser and restart the real local app using the same isolated database. Confirm the same IDs and details remain. +- [ ] For authorized live evidence, inspect those exact IDs in Atlas and compare details/flags. Record each developer's own read/write/cleanup results using the existing onboarding guide. +- [ ] Exercise a deliberately unavailable database and confirm readable failure/retry feedback without stale fixture fallback. +- [ ] Remove only demonstration records owned by the tester and verify cleanup. Any failed cleanup remains an open failure. + +Existing [management preview](./offering-preview.md) and [special-offerings evidence](./evidence/issue-17/README.md) provide controlled UI scenarios. Review their instructions and label their evidence accordingly. + +## Six-person evidence matrix + +This is an unpopulated demonstration record. Each person supplies their own evidence; agent-written runner tests do not count as their personal tests. The existing [Atlas onboarding matrix](./milestone-2-developer-guide.md#team-evidence-matrix) remains the source for individual Atlas inspection/read/write/cleanup outcomes. + +| Developer | Keyboard/narrow screen | Normal/special/invalid/save-reload | Refresh/restart | Atlas inspection/read/write/cleanup | Commit/date/artifact | Blocker | +| --------- | ---------------------- | ---------------------------------- | --------------- | ----------------------------------- | -------------------- | ------------ | +| Brian | Pending | Pending | Pending | Pending | Not recorded | Not assessed | +| Naomi | Pending | Pending | Pending | Pending | Not recorded | Not assessed | +| Jean | Pending | Pending | Pending | Pending | Not recorded | Not assessed | +| Sofie | Pending | Pending | Pending | Pending | Not recorded | Not assessed | +| Vedika | Pending | Pending | Pending | Pending | Not recorded | Not assessed | +| Aditi | Pending | Pending | Pending | Pending | Not recorded | Not assessed | + +## Acceptance criteria and handoff + +| Issue #18 criterion | Implementation/evidence | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| Retained regressions, separately named explicit loopback tooling | Existing `test:regression`; `acceptance`; safe CLI configuration/exit tests | +| Empty/seeded GET, POST/read-back, validation, IDs, errors | `scripts/acceptance.ts`; controlled HTTP positive/negative cases | +| Explicit failure setup | Deliberate database failure section and `test:acceptance:app` | +| Owned database lifecycle and cleanup refusal | `disposable-database.ts`; lifecycle, refusal, and finite teardown tests | +| Self-tests fail incorrect status/shape/read-back/teardown | Unit HTTP self-tests and separate subprocess CLI checks | +| Browser checklist | Checklist above; outcomes remain pending until demonstrated | +| Six-person matrix, commands, handoff | Matrix and commands above; Brian records integrated/live evidence under #12 | +| No persistence claims from stubs | Separate labels and commands for controlled HTTP, real local DB, and live Atlas | + +Brian can reproduce the standalone checks with `npm run test:acceptance`, the local application check with `npm run test:acceptance:app`, and full validation with `npm run preflight`. Record actual command output and commit in the PR. Independently reviewing the complete checklist and matrix satisfies the documentation deliverable; populated team rows and meeting results remain #12 work. Issue #18 may close once its independently tested tooling and complete checklist are reviewed and merged. Do not mark #12 or any pending team row complete from those results. diff --git a/docs/milestone-2-parallel-work.md b/docs/milestone-2-parallel-work.md index c46fd1a..4251045 100644 --- a/docs/milestone-2-parallel-work.md +++ b/docs/milestone-2-parallel-work.md @@ -36,7 +36,7 @@ These are implementation agreements, not claims that the modules already exist. - API: Jean's handler construction takes the storage boundary explicitly. His HTTP tests host those handlers with his own store double. Brian later composes the real route with Naomi's storage. - Form: Aditi's reusable form accepts optional specialOffer: boolean, default false. It submits that value and handles GET/POST feedback through the published HTTP contract. - Special control: Vedika's component accepts checked: boolean and onChange(next: boolean). Brian composes the control and form with shared boolean state. Neither contributor imports the other's unmerged module. -- Verification: Sofie's acceptance runner targets an explicit loopback server. Its own stub-server self-tests establish runner correctness, not real app persistence. Her disposable database lifecycle checks are independent of Naomi's model. +- Verification: Sofie's acceptance runner targets an explicit loopback server. Its own stub-server self-tests establish runner correctness, not real app persistence. Her disposable database lifecycle checks are independent of Naomi's model. Commands, deliberate failure setup, and the meeting checklist are in the [issue #18 acceptance guide](./issue-18-acceptance.md). The public API agreement remains [offering-api-contract.md](./offering-api-contract.md): no new HTTP operations, fields, identity conventions, or product scope. diff --git a/package.json b/package.json index 9e5c826..219a457 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint src tests/unit scripts/*.ts vitest.config.mts", + "lint": "eslint src tests/unit tests/acceptance-*.ts tests/controlled-server.ts scripts/*.ts vitest.config.mts", "lint:fix": "npm run lint -- --fix", "format": "prettier --write .", "preview:offerings": "node scripts/offering-preview-server.mjs", - "test": "npm run test:unit && npm run test:regression", + "test": "npm run test:unit && npm run test:regression && npm run test:acceptance:cli && npm run test:acceptance:db && npm run test:acceptance:app", "test:db": "node --test tests/database-checker.test.mjs", "db:check": "node scripts/check-database.mjs", "db:check:write": "node scripts/check-database.mjs --write", @@ -23,6 +23,12 @@ "test:storage": "node --test --import tsx tests/offering-storage.test.ts tests/offering-seed-command.test.mjs", "db:seed": "node --import tsx scripts/seed-offerings.ts", "test:live": "node --import tsx scripts/verify-live.ts", + "acceptance": "node --import tsx scripts/acceptance-runner.ts", + "acceptance:db": "node --import tsx scripts/database-smoke.ts", + "test:acceptance": "vitest run tests/unit/acceptance-runner.test.ts && npm run test:acceptance:cli && npm run test:acceptance:db", + "test:acceptance:cli": "node --test --import tsx tests/acceptance-cli.test.ts", + "test:acceptance:db": "node --test --import tsx tests/acceptance-lifecycle.test.ts", + "test:acceptance:app": "node --test --import tsx tests/acceptance-application.test.ts", "preflight": "npm run lint && npm run typecheck && npm test && npm run build" }, "dependencies": { diff --git a/scripts/acceptance-runner.ts b/scripts/acceptance-runner.ts new file mode 100644 index 0000000..bee41fb --- /dev/null +++ b/scripts/acceptance-runner.ts @@ -0,0 +1,50 @@ +import { readFile } from "node:fs/promises"; +import { runAcceptance, type AcceptanceOptions } from "./acceptance"; +import type { Offering } from "@/types/offering"; + +async function main() { + // This deadline also bounds a broken response stream or uncertain teardown. + const deadline = setTimeout(() => { + console.error( + "FAIL acceptance: overall deadline exceeded; inspect the disposable target for this run's Verification records.", + ); + process.exit(1); + }, 120000); + try { + const args = process.argv.slice(2); + if (args.length !== 2 || args[0] !== "--scenario" || !["empty", "seeded", "database-failure"].includes(args[1])) + throw new Error("Use --scenario empty|seeded|database-failure."); + if (process.env.ACCEPTANCE_CONFIRM_WRITES !== "disposable") + throw new Error("Set ACCEPTANCE_CONFIRM_WRITES=disposable only for an isolated acceptance target."); + const scenario = args[1] as AcceptanceOptions["scenario"]; + let expectedSeed: Offering[] | undefined; + if (scenario === "seeded") { + if (!process.env.ACCEPTANCE_SEED_FILE) + throw new Error("Set ACCEPTANCE_SEED_FILE to independently prepared expected seed JSON."); + try { + expectedSeed = JSON.parse(await readFile(process.env.ACCEPTANCE_SEED_FILE, "utf8")); + } catch { + throw new Error("Cannot read expected seed JSON."); + } + if (!Array.isArray(expectedSeed)) throw new Error("Expected seed JSON must be an array."); + } + const stages = await runAcceptance({ + baseUrl: process.env.ACCEPTANCE_BASE_URL ?? "", + scenario, + username: process.env.MANAGEMENT_USERNAME ?? "", + password: process.env.MANAGEMENT_PASSWORD ?? "", + expectedSeed, + }); + // Never print URLs, paths, authorization, environment values, or raw exceptions. + console.log( + `INFO acceptance: explicit literal loopback target; scenario=${scenario}; request deadline=15s; overall deadline=120s; disposable writes acknowledged.`, + ); + stages.forEach((stage) => console.log(`PASS acceptance: ${stage}`)); + } catch (error) { + console.error(`FAIL acceptance: ${error instanceof Error ? error.message : "Verification failed."}`); + process.exitCode = 1; + } finally { + clearTimeout(deadline); + } +} +void main(); diff --git a/scripts/acceptance.ts b/scripts/acceptance.ts new file mode 100644 index 0000000..9b6a6d4 --- /dev/null +++ b/scripts/acceptance.ts @@ -0,0 +1,271 @@ +import { randomUUID } from "node:crypto"; +import type { CreateOfferingInput, Offering } from "@/types/offering"; + +export type AcceptanceOptions = { + baseUrl: string; + scenario: "empty" | "seeded" | "database-failure"; + username: string; + password: string; + expectedSeed?: Offering[]; + timeoutMs?: number; +}; + +function check(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(message); +} + +/** Validate independently of application response guards and input normalization. */ +function publicOffering(value: unknown): asserts value is Offering { + check(value && typeof value === "object" && !Array.isArray(value), "Invalid public Offering."); + const item = value as Record; + check( + Object.keys(item).sort().join(",") === "ID,category,description,name,price,specialOffer", + "Invalid public fields.", + ); + check(typeof item.ID === "string" && item.ID.trim().length > 0, "Invalid public ID."); + for (const [field, limit] of [ + ["name", 100], + ["description", 500], + ] as const) { + const text = item[field]; + check( + typeof text === "string" && text === text.trim() && text.length > 0 && text.length <= limit, + "Invalid public text.", + ); + } + check( + typeof item.price === "number" && + Number.isFinite(item.price) && + item.price >= 0 && + Math.abs(item.price * 100 - Math.round(item.price * 100)) < 0.000001, + "Invalid public price.", + ); + check( + typeof item.category === "string" && + ["Coffee", "Tea", "Smoothie"].includes(item.category) && + typeof item.specialOffer === "boolean", + "Invalid category or special flag.", + ); +} + +function sameOffering(actual: Offering, expected: Offering) { + check( + Object.entries(expected).every(([key, value]) => actual[key as keyof Offering] === value), + "Offering details changed on read-back.", + ); +} + +export async function runAcceptance(options: AcceptanceOptions) { + let target: URL; + try { + target = new URL(options.baseUrl); + } catch { + throw new Error("Configure an explicit loopback origin."); + } + check( + ["127.0.0.1", "[::1]"].includes(target.hostname) && + ["http:", "https:"].includes(target.protocol) && + target.pathname === "/" && + !target.username && + !target.password && + !target.search && + !target.hash, + "Configure a literal loopback origin without credentials, paths, or query parameters.", + ); + check(["empty", "seeded", "database-failure"].includes(options.scenario), "Select an explicit scenario."); + check(options.username && options.password, "Configure private management credentials."); + const timeout = options.timeoutMs ?? 15000; + check(Number.isFinite(timeout) && timeout > 0 && timeout <= 15000, "Invalid request deadline."); + const headers = { + "Content-Type": "application/json", + Authorization: `Basic ${Buffer.from(`${options.username}:${options.password}`).toString("base64")}`, + }; + const stages: string[] = []; + async function request(method = "GET", body?: string, contentType = "application/json", path = "/api/offerings") { + try { + const response = await fetch(new URL(path, target), { + method, + headers: method === "GET" ? {} : { ...headers, "Content-Type": contentType }, + body, + redirect: "error", + signal: AbortSignal.timeout(timeout), + }); + if (response.status === 204) return { status: response.status, body: undefined }; + check(response.headers.get("content-type")?.split(";")[0].trim() === "application/json", "Response is not JSON."); + const value: unknown = await response.json(); + return { status: response.status, body: value }; + } catch { + throw new Error("HTTP request, deadline, or JSON response failed."); + } + } + async function read() { + const result = await request(); + check(result.status === 200 && Array.isArray(result.body), "GET must return 200 and an array."); + result.body.forEach(publicOffering); + const records = result.body as Offering[]; + check(new Set(records.map((item) => item.ID)).size === records.length, "Duplicate public IDs."); + for (let index = 1; index < records.length; index++) { + const previous = records[index - 1], + current = records[index]; + check( + previous.name.localeCompare(current.name) < 0 || + (previous.name.localeCompare(current.name) === 0 && previous.ID.localeCompare(current.ID) <= 0), + "GET must sort by name then ID.", + ); + } + return records; + } + async function expectError( + method: string, + body: string | undefined, + status: number, + code: string, + contentType = "application/json", + ) { + const result = await request(method, body, contentType); + check(result.status === status, `Expected ${status} for ${code}.`); + check(result.body && typeof result.body === "object" && !Array.isArray(result.body), "Invalid error envelope."); + const envelope = result.body as { error?: Record }; + check( + Object.keys(envelope).join() === "error" && + envelope.error && + typeof envelope.error === "object" && + !Array.isArray(envelope.error), + "Invalid error envelope.", + ); + check( + envelope.error.code === code && typeof envelope.error.message === "string" && envelope.error.message.trim(), + "Invalid error code or message.", + ); + check( + Object.keys(envelope.error).every((key) => ["code", "message", "fields"].includes(key)), + "Internal error fields leaked.", + ); + if (envelope.error.fields !== undefined) { + const fields = envelope.error.fields; + check(fields && typeof fields === "object" && !Array.isArray(fields), "Invalid field messages."); + check( + Object.entries(fields).every( + ([key, value]) => + ["name", "description", "price", "category", "specialOffer"].includes(key) && + typeof value === "string" && + value.trim(), + ), + "Invalid field messages.", + ); + } + check( + !/mongodb(?:\+srv)?:\/\/|password|credential|ECONNREFUSED|MongoServer|\bat .+\(.+:\d+/i.test( + JSON.stringify(envelope), + ), + "Database details leaked.", + ); + } + const input: CreateOfferingInput = { + name: `Verification ${randomUUID()}`, + description: `Runner ${randomUUID()}`, + price: 5.75, + category: "Coffee", + }; + if (options.scenario === "database-failure") { + await expectError("GET", undefined, 503, "DATABASE_UNAVAILABLE"); + await expectError("POST", JSON.stringify(input), 503, "DATABASE_UNAVAILABLE"); + return ["deliberate database failure: GET/POST sanitized 503"]; + } + const baseline = await read(); + if (options.scenario === "empty") check(baseline.length === 0, "Empty scenario requires an empty database."); + else { + check( + options.expectedSeed && options.expectedSeed.length > 0, + "Seeded scenario requires independent expected seed JSON.", + ); + check(baseline.length === options.expectedSeed.length, "Seed count differs."); + options.expectedSeed.forEach((item, index) => { + publicOffering(item); + sameOffering(baseline[index], item); + }); + } + stages.push(`${options.scenario} GET and public contract`); + const baselineIds = new Set(baseline.map((item) => item.ID)); + const createdIds = new Set(); + const attempted: CreateOfferingInput[] = []; + try { + const variants: CreateOfferingInput[] = [ + { ...input, specialOffer: false }, + { ...input, name: `${input.name} special`, specialOffer: true }, + { ...input, name: `${input.name} default` }, + { ...input, name: " n ", description: " d ", price: 0, category: "Tea" }, + { ...input, name: "n".repeat(100), description: "d".repeat(500), price: 0.29, category: "Smoothie" }, + ]; + for (const value of variants) { + attempted.push(value); + const result = await request("POST", JSON.stringify(value)); + check(result.status === 201, "POST must return 201."); + publicOffering(result.body); + const saved = result.body; + check(!baselineIds.has(saved.ID) && !createdIds.has(saved.ID), "POST reused an ID."); + createdIds.add(saved.ID); + sameOffering(saved, { + ...value, + name: value.name.trim(), + description: value.description.trim(), + specialOffer: value.specialOffer ?? false, + ID: saved.ID, + }); + const persisted = (await read()).find((item) => item.ID === saved.ID); + check(persisted, "Created Offering is missing on read-back."); + sameOffering(persisted, saved); + } + stages.push("ordinary/special/default POST, trimming, boundaries and read-back"); + const invalid: unknown[] = [ + null, + [], + "text", + {}, + ...["", " ", "n".repeat(101), 3, null].map((name) => ({ ...input, name })), + ...["", " ", "d".repeat(501), 3, null].map((description) => ({ ...input, description })), + ...[-1, 1.001, "5.75", null, true].map((price) => ({ ...input, price })), + ...["coffee", "Other", null, 1, ["Coffee"]].map((category) => ({ ...input, category })), + ...["true", null, 1].map((specialOffer) => ({ ...input, specialOffer })), + ...["ID", "_id", "unknown"].map((field) => ({ ...input, [field]: "forbidden" })), + ]; + for (const value of invalid) await expectError("POST", JSON.stringify(value), 400, "INVALID_INPUT"); + await expectError( + "POST", + JSON.stringify({ ...input, price: 1 }).replace('"price":1', '"price":1e999'), + 400, + "INVALID_INPUT", + ); + await expectError("POST", "{broken", 400, "INVALID_JSON"); + await expectError("POST", JSON.stringify(input), 415, "INVALID_CONTENT_TYPE", "text/plain"); + const finalRecords = await read(); + check(finalRecords.length === baseline.length + createdIds.size, "Invalid input changed the collection."); + stages.push("validation boundaries, content type and JSON errors"); + } finally { + // Recover uncertain POST outcomes only by this run's details and non-baseline identity. + const records = await read(); + const own = records.filter( + (item) => + !baselineIds.has(item.ID) && + attempted.some((value) => item.name === value.name.trim() && item.description === value.description.trim()), + ); + for (const item of own) { + const result = await request( + "DELETE", + undefined, + "application/json", + `/api/offerings/${encodeURIComponent(item.ID)}`, + ); + check(result.status === 204, "Targeted cleanup failed."); + } + const remaining = await read(); + check(!remaining.some((item) => own.some((removed) => removed.ID === item.ID)), "Cleanup did not persist."); + check(remaining.length === baseline.length, "Cleanup changed the baseline."); + baseline.forEach((item) => { + const preserved = remaining.find((record) => record.ID === item.ID); + check(preserved, "Cleanup removed baseline data."); + sameOffering(preserved, item); + }); + } + return [...stages, "targeted cleanup and baseline preservation"]; +} diff --git a/scripts/database-smoke.ts b/scripts/database-smoke.ts new file mode 100644 index 0000000..990d584 --- /dev/null +++ b/scripts/database-smoke.ts @@ -0,0 +1,20 @@ +import { verifyDisposableDatabase } from "./disposable-database"; + +async function main() { + const deadline = setTimeout(() => { + console.error("FAIL disposable lifecycle: overall deadline exceeded."); + process.exit(1); + }, 90000); + try { + if (process.argv.length !== 2 || process.env.MONGO_URI) throw new Error("external target"); + (await verifyDisposableDatabase()).forEach((stage) => console.log(`PASS disposable lifecycle: ${stage}`)); + } catch { + console.error( + "FAIL disposable lifecycle: no external MONGO_URI or arguments are allowed; startup, read/write, cleanup and teardown must all succeed. No connection details printed.", + ); + process.exitCode = 1; + } finally { + clearTimeout(deadline); + } +} +void main(); diff --git a/scripts/disposable-database.ts b/scripts/disposable-database.ts new file mode 100644 index 0000000..9612df9 --- /dev/null +++ b/scripts/disposable-database.ts @@ -0,0 +1,72 @@ +import { randomUUID } from "node:crypto"; +import { MongoMemoryServer } from "mongodb-memory-server-core"; +import mongoose from "mongoose"; + +/** Enforce deadlines on lifecycle operations, including cleanup. */ +export async function bounded(operation: Promise, stage: string, timeoutMs = 15000): Promise { + let timer: ReturnType | undefined; + try { + return await Promise.race([ + operation, + new Promise((_, reject) => { + timer = setTimeout(() => reject(new Error(`${stage} deadline exceeded.`)), timeoutMs); + }), + ]); + } finally { + clearTimeout(timer); + } +} + +/** No URI argument exists: every destructive operation targets a process owned here. */ +export async function verifyDisposableDatabase() { + const name = `coffee_acceptance_${randomUUID().replaceAll("-", "")}`; + const server = new MongoMemoryServer({ + binary: { version: "7.0.24" }, + instance: { dbName: name, ip: "127.0.0.1", launchTimeout: 20000 }, + }); + const connection = mongoose.createConnection(); + const stages: string[] = []; + try { + await bounded(server.start(), "database start", 60000); + await bounded( + connection.openUri(server.getUri(name), { + serverSelectionTimeoutMS: 5000, + connectTimeoutMS: 5000, + socketTimeoutMS: 5000, + }), + "database connect", + ); + stages.push("owned disposable database started"); + const collection = connection.collection("lifecycle"); + const marker = randomUUID(); + const unrelated = randomUUID(); + await bounded( + collection.insertMany([ + { marker, value: "acceptance lifecycle" }, + { marker: unrelated, value: "preserve" }, + ]), + "write", + ); + const saved = await bounded(collection.findOne({ marker }), "read"); + if (saved?.value !== "acceptance lifecycle") throw new Error("Read-back differs."); + stages.push("write and read-back"); + const deleted = await bounded(collection.deleteOne({ marker }), "cleanup"); + if (deleted.deletedCount !== 1 || (await bounded(collection.countDocuments({ marker }), "cleanup read")) !== 0) + throw new Error("Cleanup did not persist."); + if ((await bounded(collection.countDocuments({ marker: unrelated }), "preservation read")) !== 1) + throw new Error("Cleanup removed unrelated data."); + stages.push("targeted cleanup and unrelated-record preservation"); + } finally { + // Always attempt both resources even if one teardown fails. + const outcomes = await Promise.allSettled([ + bounded(connection.close(), "connection teardown"), + bounded(server.stop({ doCleanup: true, force: true }), "server teardown"), + ]); + if ( + outcomes.some((outcome) => outcome.status === "rejected") || + (outcomes[1].status === "fulfilled" && outcomes[1].value !== true) + ) + throw new Error("Disposable database teardown failed."); + } + return [...stages, "connection closed and owned server stopped"]; +} diff --git a/tests/acceptance-application.test.ts b/tests/acceptance-application.test.ts new file mode 100644 index 0000000..8df7af2 --- /dev/null +++ b/tests/acceptance-application.test.ts @@ -0,0 +1,20 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { startHttpServer } from "./helpers/http-server.mjs"; +import { runAcceptance } from "../scripts/acceptance"; + +test( + "acceptance observes real HTTP POST/read-back in an owned empty local database and deliberate failure", + { timeout: 180000 }, + async () => { + const server = await startHttpServer({ seed: false }); + const options = { baseUrl: server.baseUrl, username: "team", password: "test-only-password" }; + try { + assert.equal((await runAcceptance({ ...options, scenario: "empty" })).length, 4); + await server.stopDatabase(); + assert.equal((await runAcceptance({ ...options, scenario: "database-failure" })).length, 1); + } finally { + await server.stop(); + } + }, +); diff --git a/tests/acceptance-cli.test.ts b/tests/acceptance-cli.test.ts new file mode 100644 index 0000000..4d63341 --- /dev/null +++ b/tests/acceptance-cli.test.ts @@ -0,0 +1,115 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { test } from "node:test"; +import { startControlledServer } from "./controlled-server"; + +async function cli(script: string, args: string[], variables: Record = {}) { + return new Promise<{ code: number | null; output: string }>((resolve, reject) => { + const child = spawn(process.execPath, ["--import", "tsx", script, ...args], { + env: { + ...process.env, + MONGO_URI: "", + ACCEPTANCE_BASE_URL: "", + ACCEPTANCE_SEED_FILE: "", + ACCEPTANCE_CONFIRM_WRITES: "disposable", + MANAGEMENT_USERNAME: "team", + MANAGEMENT_PASSWORD: "test-only-password", + ...variables, + }, + windowsHide: true, + }); + let output = ""; + const timer = setTimeout(() => { + child.kill(); + reject(new Error("CLI did not terminate.")); + }, 20000); + child.stdout.on("data", (data) => { + output = (output + data).slice(-8000); + }); + child.stderr.on("data", (data) => { + output = (output + data).slice(-8000); + }); + child.on("error", (error) => { + clearTimeout(timer); + reject(error); + }); + child.on("close", (code) => { + clearTimeout(timer); + resolve({ code, output }); + }); + }); +} + +test("acceptance CLI reports success for independently controlled expected HTTP responses", async () => { + const server = await startControlledServer(); + try { + const result = await cli("scripts/acceptance-runner.ts", ["--scenario", "empty"], { + ACCEPTANCE_BASE_URL: server.baseUrl, + }); + assert.equal(result.code, 0, result.output); + assert.match(result.output, /PASS acceptance: targeted cleanup/); + assert.doesNotMatch(result.output, /test-only-password|dGVhbTp|127\.0\.0\.1/); + } finally { + await server.stop(); + } +}); + +test("acceptance CLI exits nonzero for wrong status, shape, missing read-back and failed teardown", async () => { + for (const mode of ["wrong-status", "bad-post-body", "missing-read-back", "cleanup-failure"]) { + const server = await startControlledServer(mode); + try { + const result = await cli("scripts/acceptance-runner.ts", ["--scenario", "empty"], { + ACCEPTANCE_BASE_URL: server.baseUrl, + }); + assert.equal(result.code, 1, `${mode}: ${result.output}`); + assert.match(result.output, /FAIL acceptance/); + assert.doesNotMatch(result.output, /test-only-password|dGVhbTp/); + } finally { + await server.stop(); + } + } +}); + +test("seeded CLI uses independent expectations and failure CLI requires deliberate unavailable responses", async () => { + for (const scenario of ["seeded", "database-failure"]) { + const server = await startControlledServer( + scenario === "seeded" ? "correct" : "database-failure", + scenario === "seeded", + ); + try { + const result = await cli("scripts/acceptance-runner.ts", ["--scenario", scenario], { + ACCEPTANCE_BASE_URL: server.baseUrl, + ACCEPTANCE_SEED_FILE: "tests/fixtures/acceptance-seed.json", + }); + assert.equal(result.code, 0, result.output); + assert.match(result.output, /PASS acceptance/); + } finally { + await server.stop(); + } + } +}); + +test("acceptance CLI rejects ambiguous scenario, missing acknowledgement and unsafe configuration safely", async () => { + for (const [args, variables] of [ + [[], {}], + [["--scenario", "empty"], { ACCEPTANCE_BASE_URL: "http://user:private-secret@127.0.0.1:3000" }], + [["--scenario", "empty"], { ACCEPTANCE_CONFIRM_WRITES: "" }], + [["--scenario", "seeded"], { ACCEPTANCE_SEED_FILE: "missing-private-file" }], + ] as [string[], Record][]) { + const result = await cli("scripts/acceptance-runner.ts", args, variables); + assert.equal(result.code, 1); + assert.doesNotMatch(result.output, /private-secret|missing-private-file|test-only-password/); + } +}); + +test("disposable lifecycle CLI refuses developer/integration URI and external cleanup arguments", async () => { + for (const [args, variables] of [ + [[], { MONGO_URI: "mongodb://user:private-secret@127.0.0.1/coffee_shop" }], + [["--uri", "mongodb://user:private-secret@127.0.0.1/coffee_integration"], {}], + ] as [string[], Record][]) { + const result = await cli("scripts/database-smoke.ts", args, variables); + assert.equal(result.code, 1); + assert.match(result.output, /No connection details printed/); + assert.doesNotMatch(result.output, /private-secret|coffee_shop|coffee_integration/); + } +}); diff --git a/tests/acceptance-lifecycle.test.ts b/tests/acceptance-lifecycle.test.ts new file mode 100644 index 0000000..79dcfd3 --- /dev/null +++ b/tests/acceptance-lifecycle.test.ts @@ -0,0 +1,33 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { MongoMemoryServer } from "mongodb-memory-server-core"; +import { bounded, verifyDisposableDatabase } from "../scripts/disposable-database"; + +test( + "owned disposable lifecycle starts, writes, reads, cleans and stops without Atlas", + { timeout: 90000 }, + async () => { + const stages = await verifyDisposableDatabase(); + assert.equal(stages.length, 4); + assert.match(stages[1], /write and read-back/); + assert.match(stages[2], /cleanup and unrelated-record preservation/); + assert.match(stages[3], /stopped/); + }, +); + +test("lifecycle fails when owned server refuses teardown", { timeout: 90000 }, async (context) => { + const stop = MongoMemoryServer.prototype.stop; + context.mock.method( + MongoMemoryServer.prototype, + "stop", + async function (this: MongoMemoryServer, ...args: Parameters) { + await stop.apply(this, args); + return false; + }, + ); + await assert.rejects(verifyDisposableDatabase(), /teardown failed/); +}); + +test("teardown deadlines fail instead of hanging", async () => { + await assert.rejects(bounded(new Promise(() => {}), "teardown", 20), /teardown deadline/); +}); diff --git a/tests/controlled-server.ts b/tests/controlled-server.ts new file mode 100644 index 0000000..2ed4919 --- /dev/null +++ b/tests/controlled-server.ts @@ -0,0 +1,112 @@ +import { createServer } from "node:http"; +import type { Offering } from "@/types/offering"; + +export const seedOffering: Offering = { + ID: "seed-id", + name: "Seed Tea", + description: "Independent seed", + price: 2.5, + category: "Tea", + specialOffer: true, +}; + +/** Runner self-test only: not the application or persistence evidence. */ +export async function startControlledServer(mode = "correct", seeded = false) { + const records: Offering[] = seeded ? [{ ...seedOffering }] : []; + let nextId = 0; + const server = createServer(async (request, response) => { + const send = (status: number, body?: unknown) => { + response.writeHead(status, { "Content-Type": "application/json" }); + response.end(body === undefined ? undefined : JSON.stringify(body)); + }; + const error = (status: number, code: string) => + send(status, { error: { code, message: "Request failed safely." } }); + if (mode === "timeout") return; + if (mode === "database-failure" || mode === "leaked-failure") { + send(503, { + error: { + code: "DATABASE_UNAVAILABLE", + message: mode === "leaked-failure" ? "mongodb://user:secret@host/db" : "Please retry later.", + }, + }); + return; + } + if (request.method === "GET") { + if (mode === "private-get" && !request.headers.authorization) return error(401, "UNAUTHORIZED"); + const ordered = [...records].sort((a, b) => a.name.localeCompare(b.name) || a.ID.localeCompare(b.ID)); + send( + mode === "wrong-status" ? 500 : 200, + mode === "wrong-shape" ? {} : mode === "wrong-order" ? ordered.reverse() : ordered, + ); + return; + } + if (request.headers.authorization !== "Basic dGVhbTp0ZXN0LW9ubHktcGFzc3dvcmQ=") return error(401, "UNAUTHORIZED"); + if (request.method === "DELETE") { + if (mode === "cleanup-failure") return error(503, "DATABASE_UNAVAILABLE"); + const id = decodeURIComponent(request.url!.split("/").at(-1)!); + const index = records.findIndex((record) => record.ID === id); + if (index >= 0 && mode !== "cleanup-not-persisted") records.splice(index, 1); + send(204); + return; + } + if (request.headers["content-type"] !== "application/json") return error(415, "INVALID_CONTENT_TYPE"); + let body: Record; + try { + let text = ""; + for await (const chunk of request) text += chunk; + body = JSON.parse(text); + } catch { + return error(400, "INVALID_JSON"); + } + const valid = + body && + typeof body === "object" && + !Array.isArray(body) && + typeof body.name === "string" && + body.name.trim().length > 0 && + body.name.trim().length <= 100 && + typeof body.description === "string" && + body.description.trim().length > 0 && + body.description.trim().length <= 500 && + typeof body.price === "number" && + Number.isFinite(body.price) && + body.price >= 0 && + Math.abs(body.price * 100 - Math.round(body.price * 100)) < 0.000001 && + typeof body.category === "string" && + ["Coffee", "Tea", "Smoothie"].includes(body.category) && + (body.specialOffer === undefined || typeof body.specialOffer === "boolean") && + Object.keys(body).every((key) => ["name", "description", "price", "category", "specialOffer"].includes(key)); + if (!valid) return mode === "accept-invalid" ? send(201, {}) : error(400, "INVALID_INPUT"); + const offering = { + ...body, + name: (body.name as string).trim(), + description: (body.description as string).trim(), + specialOffer: body.specialOffer ?? false, + ID: `created-${++nextId}`, + } as Offering; + if (mode === "wrong-default" && body.specialOffer === undefined) offering.specialOffer = true; + if (mode === "wrong-special" && body.specialOffer === true) offering.specialOffer = false; + if (mode === "invalid-id") offering.ID = ""; + if (mode === "duplicate-id") offering.ID = "reused-id"; + if (mode !== "missing-read-back") records.push(offering); + if (mode === "bad-post-body") return send(201, {}); + if (mode === "internal-fields") return send(201, { ...offering, _id: "internal-id" }); + if (mode === "malformed-json") { + response.writeHead(201, { "Content-Type": "application/json" }); + response.end("broken"); + return; + } + send(201, offering); + }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("Test listener unavailable"); + return { + baseUrl: `http://127.0.0.1:${address.port}`, + records, + async stop() { + server.closeAllConnections(); + await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); + }, + }; +} diff --git a/tests/fixtures/acceptance-seed.json b/tests/fixtures/acceptance-seed.json new file mode 100644 index 0000000..4cdc9d9 --- /dev/null +++ b/tests/fixtures/acceptance-seed.json @@ -0,0 +1,10 @@ +[ + { + "ID": "seed-id", + "name": "Seed Tea", + "description": "Independent seed", + "price": 2.5, + "category": "Tea", + "specialOffer": true + } +] diff --git a/tests/unit/acceptance-runner.test.ts b/tests/unit/acceptance-runner.test.ts new file mode 100644 index 0000000..da5fbe2 --- /dev/null +++ b/tests/unit/acceptance-runner.test.ts @@ -0,0 +1,114 @@ +import { expect, it } from "vitest"; +import { seedOffering, startControlledServer } from "../controlled-server"; +import { runAcceptance } from "../../scripts/acceptance"; + +it("runner rejects 201 with an invalid Offering even when GET succeeds", async () => { + const server = await startControlledServer("bad-post-body"); + try { + await expect( + runAcceptance({ baseUrl: server.baseUrl, scenario: "empty", username: "team", password: "test-only-password" }), + ).rejects.toThrow(/Invalid public fields/); + } finally { + await server.stop(); + } +}); + +const credentials = { username: "team", password: "test-only-password" }; +it.each(["empty", "seeded"] as const)("runner verifies the %s scenario and cleans up its records", async (scenario) => { + const server = await startControlledServer("correct", scenario === "seeded"); + try { + const stages = await runAcceptance({ + baseUrl: server.baseUrl, + scenario, + ...credentials, + expectedSeed: [seedOffering], + }); + expect(stages).toHaveLength(4); + expect(server.records).toEqual(scenario === "seeded" ? [seedOffering] : []); + } finally { + await server.stop(); + } +}); + +it.each([ + "wrong-status", + "private-get", + "wrong-shape", + "missing-read-back", + "wrong-default", + "wrong-special", + "invalid-id", + "duplicate-id", + "internal-fields", + "wrong-order", + "malformed-json", + "accept-invalid", + "cleanup-failure", + "cleanup-not-persisted", + "timeout", +])("runner fails for controlled %s behavior", async (mode) => { + const server = await startControlledServer(mode); + try { + await expect( + runAcceptance({ baseUrl: server.baseUrl, scenario: "empty", ...credentials, timeoutMs: 300 }), + ).rejects.toThrow(); + } finally { + await server.stop(); + } +}); + +it("requires an explicit seeded expectation and actual empty state", async () => { + const server = await startControlledServer("correct", true); + try { + await expect(runAcceptance({ baseUrl: server.baseUrl, scenario: "empty", ...credentials })).rejects.toThrow( + /empty database/, + ); + await expect(runAcceptance({ baseUrl: server.baseUrl, scenario: "seeded", ...credentials })).rejects.toThrow( + /expected seed/, + ); + await expect( + runAcceptance({ + baseUrl: server.baseUrl, + scenario: "seeded", + ...credentials, + expectedSeed: [{ ...seedOffering, price: 9 }], + }), + ).rejects.toThrow(/details changed/); + } finally { + await server.stop(); + } +}); + +it("checks deliberate GET and POST database failure without inferring it from ordinary mode", async () => { + const server = await startControlledServer("database-failure"); + try { + expect(await runAcceptance({ baseUrl: server.baseUrl, scenario: "database-failure", ...credentials })).toHaveLength( + 1, + ); + await expect(runAcceptance({ baseUrl: server.baseUrl, scenario: "empty", ...credentials })).rejects.toThrow(/GET/); + } finally { + await server.stop(); + } +}); + +it("rejects raw connection details in deliberately failed database responses", async () => { + const server = await startControlledServer("leaked-failure"); + try { + await expect( + runAcceptance({ baseUrl: server.baseUrl, scenario: "database-failure", ...credentials }), + ).rejects.toThrow(/leaked/); + } finally { + await server.stop(); + } +}); + +it.each([ + "https://shop.example", + "http://localhost:3000", + "http://team:secret@127.0.0.1:3000", + "http://127.0.0.1:3000/path", + "http://127.0.0.1:3000?secret=x", + "invalid", +])("refuses unsafe or ambiguous target %s before requests", async (baseUrl) => { + await expect(runAcceptance({ baseUrl, scenario: "empty", ...credentials })).rejects.toThrow(/loopback/); +});