commit ea9adb85e69f76d4bc9c428494ce8b89883c9206 Author: Christian Johannesen Date: Wed Oct 28 09:59:06 2026 +0100 Implement M0 bootable Plant Laboratory infrastructure diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..36e8cc0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +**/node_modules +**/dist +.git +.env +.env.* +coverage +*.log diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5ab629f --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +HOST_PORT=8155 +PUBLIC_DOMAIN=http://localhost +POSTGRES_DB=botanica +POSTGRES_USER=botanica +POSTGRES_PASSWORD=change-me-before-exposing +NODE_ENV=development diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b0e491 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules/ +dist/ +coverage/ +.vite/ +*.log +.env.local +.env.production +.env.*.local +!.env.example +# Local .env supplied in handoff but should not be committed to public VCS +.env diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d45aedf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,51 @@ +# Codex repository instructions — Botanica / Plant Laboratory + +## Mission +Build **Plant Laboratory**, the first working module of a future online plant-breeding idle game. Illustrated 2D, procedural SVG assembled from art-directed botanical vector primitives, deterministic genetics, and an elegant responsive interface. This repository contains the authoritative implementation handoff. **Read `docs/IMPLEMENTATION_PLAN.md` and `docs/PRODUCT_SPEC.md` first.** + +## Non-negotiable operational settings +- Docker Compose is the standard runtime. The public HTTP UI is exposed on `${HOST_PORT}` (default **8155**) at `${PUBLIC_DOMAIN}` (default **http://localhost**), thus `http://localhost:8155`. +- The user requested literal `.env` values `HOST_PORT=8155` and `PUBLIC_DOMAIN=http://localhost`. Do not silently change these values. `PUBLIC_DOMAIN` is an **origin without port**, not a full public base URL. Build `PUBLIC_BASE_URL` from origin + exposed port for local development; do not append the port in production when running behind a reverse proxy. See `docs/DEPLOYMENT.md`. +- Keep PostgreSQL and API private on Compose networks; publish only the web entrypoint. +- No paid AI APIs, external image generation at runtime, cloud-only dependencies, trackers, auth integrations, marketplaces, multiplayer or microservices in the MVP. +- Use the root `package.json` workspaces with npm and committed lockfile after installing. + +## Source of truth and reading order +1. `docs/IMPLEMENTATION_PLAN.md` — phases and acceptance gates. +2. `docs/PRODUCT_SPEC.md` — player workflows, UI, quality bar. +3. `docs/GENETICS_SPEC.md` — genotype/phenotype, inheritance and randomness. +4. `docs/GRAPHICS_SPEC.md` — art pipeline, asset contract, deterministic SVG. +5. `docs/API_AND_DATA.md` — schemas, persistence, routes and invariants. +6. `docs/DEPLOYMENT.md` — Compose, ports, secrets, runbook. +7. `docs/DECISIONS.md` — fixed architectural decisions. +8. `docs/CODEX_START_PROMPT.md` — initial mission. + +## Implementation habits +- Work through milestones **M0 → M1 → M2 → M3 → M4**, one at a time. Never substitute a large unverified implementation for milestone gates. +- Implement the current milestone end to end; run its specified tests; update the checklist in `docs/PROGRESS.md`. Stop at a gate and report results, blockers and next step. If explicitly asked to continue autonomously, proceed sequentially. +- Favor simple readable TypeScript; no speculative abstraction, no giant dependencies, no unnecessary test scaffolding. +- Implement actual code and assets, not mockups, placeholders, TODO-based behavior or screenshots-as-assets. Make the UI genuinely usable. +- `packages/genetics`, `packages/renderer`, `packages/rules` must not import React, DOM UI state, database or server. Rendering may consume DOM/SVG APIs only in a thin web adapter; pure layout remains platform independent. +- Server owns authoritative breeding decisions, economy and persisted plant creation. Client never dictates child genotype, mutation outcome, rarity or inventory balances. +- Seeded random generation must be reproducible with explicit algorithm and version; **never use `Math.random()` inside genetics/renderer domain functions**. Use cryptographically secure server RNG to generate initial seeds; store them and use a deterministic PRNG for replay. +- Preserve provenance in `parents`, `generation`, `genomeVersion`, `rendererVersion`, `visualSeed`; no deleting ancestry on migration. +- No arbitrary cross-species breeding. Require compatibility groups, with species-specific constraints. +- Avoid using rarity as a naive random roll independent from genotype. Derive it from a versioned rarity policy as outlined in the specs. +- UI: English copy in the initial codebase; architecture ready for future i18n. Accessible labels, keyboard navigation, reduced motion support. +- Use SVG vector shapes created for this project; no scraped commercial plant assets or image-hotlinking. + +## Technical defaults +React + Vite + TypeScript, Tailwind CSS, shadcn/ui where useful, Motion for transitions, Zustand for local view state, TanStack Query for API state; Node.js + Fastify + Zod, PostgreSQL + Drizzle; Docker Compose. Use current compatible stable dependency versions and pin them through lockfile; verify APIs against documentation if needed. + +## Required commands / checks +- `docker compose config` +- `docker compose up --build -d` +- `docker compose ps` +- `curl -fsS http://localhost:8155/health` +- `npm run typecheck` +- `npm run test` +- `npm run build` +Use commands as applicable during scaffolding; fix any errors rather than suppressing them. The health endpoint must be served by web reverse proxy to API. + +## Finish and reporting +After each milestone report: files changed, URL, tests actually run/results, screenshots or concise UI description, known issues, and what M milestone is next. Do not claim tests were run if they were not. Never mutate `.env` secretly, commit credentials, or delete the user's data. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4785ae8 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Plant Laboratory / Botanica + +M0 provides a running React shell, Fastify API, PostgreSQL schema and repeatable bootstrap. Genetics and game workflows follow in M1–M4. + +## Run + +Keep `.env` local. Copy `.env.example` if missing; preserve `HOST_PORT=8155` and `PUBLIC_DOMAIN=http://localhost`. + +```sh +npm ci +docker compose config --quiet +docker compose up --build -d +docker compose ps +curl -fsS http://localhost:8155/health +``` + +Open http://localhost:8155. Only the web entrypoint is published, bound to loopback. API and PostgreSQL use the Compose network. The one-shot `bootstrap` service runs a transaction-locked migration followed by an idempotent demo-player/catalog seed before API startup. A named volume retains data across `docker compose down` and `up`; do not use `down -v` unless intentionally deleting all data. + +## Development and checks + +```sh +npm run typecheck +npm run test +npm run build +npm run dev +``` + +`npm run dev` serves Vite on port 5173 and API on 3000. Set `DATABASE_URL` to a reachable PostgreSQL database for host development; Compose intentionally does not publish its database. Prefer Compose for the complete runtime. + +Root `npm run db:migrate` and `npm run db:seed` require `DATABASE_URL`. Against Compose: + +```sh +docker compose run --rm bootstrap node dist/migrate.js +docker compose run --rm bootstrap node dist/seed.js +``` + +Migrations are checked-in SQL tracked in `schema_migrations`; seeds never overwrite existing records. `.env` is ignored by Git and Docker build contexts. Production containers run as non-root. The API returns safe uniform errors and health responds 503 if PostgreSQL is unavailable. Relative browser API URLs avoid container hostname leakage. `PUBLIC_BASE_URL` is validated and derived from origin plus port for localhost, or just origin behind a production reverse proxy. + +Routes: `/` redirects to `/lab`; `/lab`, `/breed`, `/gallery`, `/about` currently show clearly labeled milestone empty states. Original shell illustration is decorative; the deterministic species renderer is M2. No authentication: this sandbox is intended for local use. + +Read [the implementation plan](docs/IMPLEMENTATION_PLAN.md) and [progress log](docs/PROGRESS.md) for acceptance gates. diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile new file mode 100644 index 0000000..f7e7853 --- /dev/null +++ b/apps/server/Dockerfile @@ -0,0 +1,20 @@ +FROM node:24-alpine AS build +WORKDIR /app +COPY package*.json ./ +COPY apps/server/package.json apps/server/package.json +COPY apps/web/package.json apps/web/package.json +COPY packages/ packages/ +RUN npm ci +COPY tsconfig.base.json ./ +COPY apps/server/ apps/server/ +RUN npm run build -w @botanica/server && npm prune --omit=dev +FROM node:24-alpine +ENV NODE_ENV=production +WORKDIR /app +COPY --from=build --chown=node:node /app/node_modules ./node_modules +COPY --from=build --chown=node:node /app/apps/server/package.json ./package.json +COPY --from=build --chown=node:node /app/apps/server/dist ./dist +COPY --from=build --chown=node:node /app/apps/server/migrations ./migrations +USER node +EXPOSE 3000 +CMD ["node", "dist/index.js"] diff --git a/apps/server/migrations/0001_initial.sql b/apps/server/migrations/0001_initial.sql new file mode 100644 index 0000000..dcd95b6 --- /dev/null +++ b/apps/server/migrations/0001_initial.sql @@ -0,0 +1,7 @@ +CREATE TABLE species (id text PRIMARY KEY, name text NOT NULL, compatibility_group text NOT NULL, catalog_version integer NOT NULL, config_jsonb jsonb NOT NULL); +CREATE TABLE players (id uuid PRIMARY KEY, display_name text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()); +CREATE TABLE plants (id uuid PRIMARY KEY, owner_id uuid NOT NULL REFERENCES players(id), species_id text NOT NULL REFERENCES species(id), genome_version integer NOT NULL, genome jsonb NOT NULL, mutations jsonb NOT NULL, visual_seed integer NOT NULL, renderer_version integer NOT NULL, rarity_policy_version integer NOT NULL, rarity_score integer NOT NULL CHECK (rarity_score BETWEEN 0 AND 100), rarity_tier text NOT NULL CHECK (rarity_tier IN ('Common','Uncommon','Rare','Epic','Legendary','Mythic')), generation integer NOT NULL CHECK (generation >= 0), parent_a_id uuid REFERENCES plants(id), parent_b_id uuid REFERENCES plants(id), created_at timestamptz NOT NULL DEFAULT now(), CHECK ((parent_a_id IS NULL) = (parent_b_id IS NULL))); +CREATE INDEX plants_owner_created ON plants(owner_id, created_at DESC); +CREATE INDEX plants_species_rarity ON plants(species_id, rarity_tier); +CREATE TABLE breeding_events (id uuid PRIMARY KEY, owner_id uuid NOT NULL REFERENCES players(id), parent_a_id uuid NOT NULL REFERENCES plants(id), parent_b_id uuid NOT NULL REFERENCES plants(id), child_id uuid NOT NULL UNIQUE REFERENCES plants(id), rng_seed integer NOT NULL, engine_version integer NOT NULL, mutation_events jsonb NOT NULL, idempotency_key text NOT NULL UNIQUE, created_at timestamptz NOT NULL DEFAULT now()); +CREATE INDEX breeding_owner_created ON breeding_events(owner_id, created_at DESC); diff --git a/apps/server/package.json b/apps/server/package.json new file mode 100644 index 0000000..01211cb --- /dev/null +++ b/apps/server/package.json @@ -0,0 +1,23 @@ +{ + "name": "@botanica/server", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "db:migrate": "tsx src/migrate.ts", + "db:seed": "tsx src/seed.ts" + }, + "dependencies": { + "fastify": "^5.12.7", + "zod": "^4.0.0", + "drizzle-orm": "^0.45.4", + "pg": "^8.16.3" + }, + "devDependencies": { + "@types/pg": "^8.15.5", + "tsx": "^4.20.0" + } +} diff --git a/apps/server/src/app.test.ts b/apps/server/src/app.test.ts new file mode 100644 index 0000000..27b9205 --- /dev/null +++ b/apps/server/src/app.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect } from "vitest"; +import { createApp } from "./app.js"; +import { readConfig } from "./config.js"; +describe("M0 infrastructure contracts", () => { + it("checks database before reporting healthy", async () => { + let queries = 0; + const app = createApp(async () => { + queries++; + }); + const response = await app.inject("/health"); + expect(response.statusCode).toBe(200); + expect(response.json()).toEqual({ status: "ok", database: "connected" }); + expect(queries).toBe(1); + await app.close(); + }); + it("returns safe database and route errors", async () => { + const app = createApp(async () => { + throw new Error("secret password"); + }); + const response = await app.inject("/health"); + expect(response.statusCode).toBe(503); + expect(response.body).not.toContain("secret"); + expect((await app.inject("/missing")).json().error.code).toBe("NOT_FOUND"); + await app.close(); + }); + it("derives local URL and preserves reverse proxy origin", () => { + expect(readConfig({ DATABASE_URL: "postgres://db" }).PUBLIC_BASE_URL).toBe( + "http://localhost:8155", + ); + expect( + readConfig({ + DATABASE_URL: "postgres://db", + PUBLIC_DOMAIN: "https://plants.example", + }).PUBLIC_BASE_URL, + ).toBe("https://plants.example"); + expect(() => + readConfig({ DATABASE_URL: "x", PUBLIC_DOMAIN: "http://localhost:8155" }), + ).toThrow(); + }); +}); diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts new file mode 100644 index 0000000..efd1e7a --- /dev/null +++ b/apps/server/src/app.ts @@ -0,0 +1,48 @@ +import Fastify from "fastify"; +export function createApp( + checkDatabase: () => Promise, + logger = false, +) { + const app = Fastify({ logger, bodyLimit: 65536 }); + app.get("/health", async (_request, reply) => { + try { + await checkDatabase(); + return { status: "ok", database: "connected" }; + } catch { + return reply + .code(503) + .send({ + error: { + code: "DATABASE_UNAVAILABLE", + message: "Database is unavailable.", + }, + }); + } + }); + app.setNotFoundHandler((_request, reply) => + reply + .code(404) + .send({ error: { code: "NOT_FOUND", message: "Route not found." } }), + ); + app.setErrorHandler((error, request, reply) => { + request.log.error(error); + const code = + typeof error === "object" && error !== null && "statusCode" in error + ? error.statusCode + : undefined; + const status = + typeof code === "number" && code >= 400 && code < 500 ? code : 500; + reply + .code(status) + .send({ + error: { + code: status === 500 ? "INTERNAL_ERROR" : "INVALID_REQUEST", + message: + status === 500 + ? "An unexpected error occurred." + : "Request could not be processed.", + }, + }); + }); + return app; +} diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts new file mode 100644 index 0000000..57709d8 --- /dev/null +++ b/apps/server/src/config.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; +export function readConfig(env: NodeJS.ProcessEnv) { + const c = z + .object({ + DATABASE_URL: z.string().min(1), + PORT: z.coerce.number().int().min(1).max(65535).default(3000), + HOST_PORT: z.coerce.number().int().min(1).max(65535).default(8155), + PUBLIC_DOMAIN: z.string().url().default("http://localhost"), + }) + .parse(env); + const origin = new URL(c.PUBLIC_DOMAIN); + if ( + origin.port || + origin.pathname !== "/" || + origin.search || + origin.hash || + origin.username || + origin.password + ) + throw new Error("PUBLIC_DOMAIN must be an origin without port"); + return { + ...c, + PUBLIC_BASE_URL: ["localhost", "127.0.0.1", "[::1]"].includes( + origin.hostname, + ) + ? origin.origin + ":" + c.HOST_PORT + : origin.origin, + }; +} diff --git a/apps/server/src/db.ts b/apps/server/src/db.ts new file mode 100644 index 0000000..f22d8f2 --- /dev/null +++ b/apps/server/src/db.ts @@ -0,0 +1,11 @@ +import pg from "pg"; +import { drizzle } from "drizzle-orm/node-postgres"; +import * as schema from "./schema.js"; +export function connectDatabase(url: string) { + const pool = new pg.Pool({ + connectionString: url, + max: 10, + connectionTimeoutMillis: 5000, + }); + return { pool, db: drizzle(pool, { schema }) }; +} diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts new file mode 100644 index 0000000..f47cf1b --- /dev/null +++ b/apps/server/src/index.ts @@ -0,0 +1,14 @@ +import { readConfig } from "./config.js"; +import { connectDatabase } from "./db.js"; +import { createApp } from "./app.js"; +const config = readConfig(process.env); +const { pool } = connectDatabase(config.DATABASE_URL); +const app = createApp(() => pool.query("SELECT 1"), true); +app.addHook("onClose", async () => { + await pool.end(); +}); +for (const signal of ["SIGINT", "SIGTERM"]) + process.on(signal, () => { + void app.close(); + }); +await app.listen({ port: config.PORT, host: "0.0.0.0" }); diff --git a/apps/server/src/migrate.ts b/apps/server/src/migrate.ts new file mode 100644 index 0000000..588e2c8 --- /dev/null +++ b/apps/server/src/migrate.ts @@ -0,0 +1,35 @@ +import { readFile } from "node:fs/promises"; +import { connectDatabase } from "./db.js"; +const { pool } = connectDatabase(process.env.DATABASE_URL ?? ""); +const client = await pool.connect(); +try { + await client.query("BEGIN"); + await client.query("SELECT pg_advisory_xact_lock(8155000)"); + await client.query( + "CREATE TABLE IF NOT EXISTS schema_migrations (name text PRIMARY KEY, applied_at timestamptz NOT NULL DEFAULT now())", + ); + const name = "0001_initial"; + const exists = await client.query( + "SELECT name FROM schema_migrations WHERE name = $1", + [name], + ); + if (!exists.rowCount) { + await client.query( + await readFile( + new URL("../migrations/0001_initial.sql", import.meta.url), + "utf8", + ), + ); + await client.query("INSERT INTO schema_migrations(name) VALUES ($1)", [ + name, + ]); + } + await client.query("COMMIT"); + console.log("Migrations current"); +} catch (error) { + await client.query("ROLLBACK"); + throw error; +} finally { + client.release(); + await pool.end(); +} diff --git a/apps/server/src/schema.ts b/apps/server/src/schema.ts new file mode 100644 index 0000000..8c5e25b --- /dev/null +++ b/apps/server/src/schema.ts @@ -0,0 +1,85 @@ +import { + pgTable, + text, + integer, + uuid, + jsonb, + timestamp, + index, + unique, + type AnyPgColumn, +} from "drizzle-orm/pg-core"; +export const species = pgTable("species", { + id: text().primaryKey(), + name: text().notNull(), + compatibilityGroup: text("compatibility_group").notNull(), + catalogVersion: integer("catalog_version").notNull(), + config: jsonb("config_jsonb").notNull(), +}); +export const players = pgTable("players", { + id: uuid().primaryKey(), + displayName: text("display_name").notNull(), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), +}); +export const plants = pgTable( + "plants", + { + id: uuid().primaryKey(), + ownerId: uuid("owner_id") + .notNull() + .references(() => players.id), + speciesId: text("species_id") + .notNull() + .references(() => species.id), + genomeVersion: integer("genome_version").notNull(), + genome: jsonb().notNull(), + mutations: jsonb().notNull(), + visualSeed: integer("visual_seed").notNull(), + rendererVersion: integer("renderer_version").notNull(), + rarityPolicyVersion: integer("rarity_policy_version").notNull(), + rarityScore: integer("rarity_score").notNull(), + rarityTier: text("rarity_tier").notNull(), + generation: integer().notNull(), + parentAId: uuid("parent_a_id").references((): AnyPgColumn => plants.id), + parentBId: uuid("parent_b_id").references((): AnyPgColumn => plants.id), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (t) => [ + index("plants_owner_created").on(t.ownerId, t.createdAt.desc()), + index("plants_species_rarity").on(t.speciesId, t.rarityTier), + ], +); +export const breedingEvents = pgTable( + "breeding_events", + { + id: uuid().primaryKey(), + ownerId: uuid("owner_id") + .notNull() + .references(() => players.id), + parentAId: uuid("parent_a_id") + .notNull() + .references(() => plants.id), + parentBId: uuid("parent_b_id") + .notNull() + .references(() => plants.id), + childId: uuid("child_id") + .notNull() + .references(() => plants.id), + rngSeed: integer("rng_seed").notNull(), + engineVersion: integer("engine_version").notNull(), + mutationEvents: jsonb("mutation_events").notNull(), + idempotencyKey: text("idempotency_key").notNull(), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (t) => [ + unique().on(t.childId), + unique().on(t.idempotencyKey), + index("breeding_owner_created").on(t.ownerId, t.createdAt.desc()), + ], +); diff --git a/apps/server/src/seed.ts b/apps/server/src/seed.ts new file mode 100644 index 0000000..8155343 --- /dev/null +++ b/apps/server/src/seed.ts @@ -0,0 +1,37 @@ +import { connectDatabase } from "./db.js"; +import { players, species } from "./schema.js"; +const { pool, db } = connectDatabase(process.env.DATABASE_URL ?? ""); +try { + await db.transaction(async (tx) => { + await tx + .insert(players) + .values({ + id: "00000000-0000-4000-8000-000000000001", + displayName: "Sandbox botanist", + }) + .onConflictDoNothing(); + await tx + .insert(species) + .values({ + id: "velvetleaf", + name: "Velvetleaf", + compatibilityGroup: "velvetleaf", + catalogVersion: 1, + config: { + interspeciesBreeding: false, + loci: { + pigment: ["G", "P", "R", "Y"], + leafShape: ["O", "L", "S"], + leafPattern: ["S", "V", "M"], + growthHabit: ["C", "B", "T"], + flowerColor: ["W", "P", "B", "R"], + flowerDensity: ["L", "M", "H"], + }, + }, + }) + .onConflictDoNothing(); + }); + console.log("Sandbox player and velvetleaf catalog ready"); +} finally { + await pool.end(); +} diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json new file mode 100644 index 0000000..5edad81 --- /dev/null +++ b/apps/server/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 0000000..7a2cd52 --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,14 @@ +FROM node:24-alpine AS build +WORKDIR /app +COPY package*.json ./ +COPY apps/server/package.json apps/server/package.json +COPY apps/web/package.json apps/web/package.json +COPY packages/ packages/ +RUN npm ci +COPY tsconfig.base.json ./ +COPY apps/web/ apps/web/ +RUN npm run build -w @botanica/web +FROM nginxinc/nginx-unprivileged:stable-alpine +COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/apps/web/dist /usr/share/nginx/html +EXPOSE 8080 diff --git a/apps/web/components.json b/apps/web/components.json new file mode 100644 index 0000000..b4d8889 --- /dev/null +++ b/apps/web/components.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "css": "src/styles.css", + "baseColor": "neutral", + "cssVariables": true + }, + "aliases": { + "components": "./src/components", + "utils": "./src/lib/utils" + } +} diff --git a/apps/web/index.html b/apps/web/index.html new file mode 100644 index 0000000..15b3bf3 --- /dev/null +++ b/apps/web/index.html @@ -0,0 +1 @@ +Plant Laboratory
diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf new file mode 100644 index 0000000..36c5db7 --- /dev/null +++ b/apps/web/nginx.conf @@ -0,0 +1,12 @@ +server { + listen 8080; + server_name _; + root /usr/share/nginx/html; + index index.html; + server_tokens off; + add_header X-Content-Type-Options nosniff always; + add_header Referrer-Policy same-origin always; + location = /health { proxy_pass http://api:3000/health; } + location /api/ { proxy_pass http://api:3000; } + location / { try_files $uri $uri/ /index.html; } +} diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..5881be6 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,30 @@ +{ + "name": "@botanica/web", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --host 0.0.0.0", + "build": "tsc --noEmit && vite build", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "react": "^19.3.1", + "react-dom": "^19.3.1", + "react-router": "^7.0.0", + "zustand": "^5.0.0", + "@tanstack/react-query": "^5.0.0", + "clsx": "^2.1.1", + "tailwind-merge": "^3.0.0", + "class-variance-authority": "^0.7.1", + "lucide-react": "^0.577.0" + }, + "devDependencies": { + "vite": "^8.3.5", + "@vitejs/plugin-react": "^6.0.0", + "tailwindcss": "^4.0.0", + "@tailwindcss/vite": "^4.0.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0" + } +} diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts new file mode 100644 index 0000000..109c4e2 --- /dev/null +++ b/apps/web/src/lib/utils.ts @@ -0,0 +1,5 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx new file mode 100644 index 0000000..2288f9a --- /dev/null +++ b/apps/web/src/main.tsx @@ -0,0 +1,265 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import { BrowserRouter, NavLink, Navigate, Route, Routes } from "react-router"; +import { + QueryClient, + QueryClientProvider, + useQuery, +} from "@tanstack/react-query"; +import { + Leaf, + ArrowUpRight, + FlaskConical, + Sprout, + Images, + BookOpen, +} from "lucide-react"; +import "./styles.css"; +const queryClient = new QueryClient(); +const pages = { + lab: { + label: "Laboratory", + eyebrow: "A place for possibility", + title: "Small seeds. Endless possibilities.", + description: + "A botanical cabinet for curious minds. Explore the art of growing, the patterns of inheritance, and the delight of something unexpected.", + empty: "Your workbench is taking root", + detail: + "The laboratory is ready for its first species. Genome controls and live plant previews arrive in the next milestones.", + icon: FlaskConical, + }, + breed: { + label: "Breeding", + eyebrow: "The next generation", + title: "Every cross begins a new story.", + description: + "Bring two compatible plants together and follow the traits they pass on.", + empty: "No parents to pair yet", + detail: + "Saved parent plants and authoritative breeding will arrive in milestone M3.", + icon: Sprout, + }, + gallery: { + label: "Collection", + eyebrow: "Your living archive", + title: "A collection worth cultivating.", + description: + "A home for each discovery, from familiar greens to the wonderfully unusual.", + empty: "Room for your first discovery", + detail: + "Your saved plants will appear here. The collection gallery arrives in milestone M4.", + icon: Images, + }, + about: { + label: "Field notes", + eyebrow: "About this experiment", + title: "Botany meets a little imagination.", + description: + "Plant Laboratory is the first chapter of Botanica, a future plant-breeding idle game.", + empty: "Fictional genetics, real curiosity", + detail: + "This educational game uses simplified, fictional Mendelian genetics. It is not a scientifically accurate botany simulator. This local sandbox has no accounts yet.", + icon: BookOpen, + }, +}; +function BotanicalPlate() { + return ( + + + + + + + + + + + + {[ + [171, 251, -52], + [315, 209, 45], + [199, 160, -41], + [297, 127, 35], + [252, 108, 0], + ].map(([x, y, r], i) => ( + + + + + + ))} + + + + + + + Fig. 01 — A study in green + + + ); +} +function Health() { + const result = useQuery({ + queryKey: ["health"], + queryFn: async () => { + const r = await fetch("/health"); + if (!r.ok) throw new Error("Unavailable"); + return r.json() as Promise<{ status: string }>; + }, + refetchInterval: 30000, + retry: 1, + }); + return ( + + + {result.isPending + ? "Connecting to laboratory" + : result.isError + ? "Laboratory connection unavailable" + : "Laboratory online"} + + ); +} +function Page({ page }: { page: keyof typeof pages }) { + const p = pages[page]; + const Icon = p.icon; + return ( + <> +
+
+

{p.eyebrow}

+

{p.title}

+

{p.description}

+
+ PLANT LABORATORY + Local sandbox · No. 001 +
+
+
+ +
BOTANICAL STUDY / ILLUSTRATED SHELL ART
+
+
+
+
+
+

{p.label}

+

+ {page === "lab" ? "The workbench" : p.label} +

+
+ M0 · Foundations +
+
+ + + +

{p.empty}

+

{p.detail}

+ {page === "lab" && ( + + Read the field notes + + )} +
+
+ + ); +} +function App() { + return ( + <> + + Skip to content + +
+ + + + + + Plant LaboratoryBOTANICA / EXPERIMENT NO. 001 + + + +
+
+ + } /> + {Object.keys(pages).map((key) => ( + } + /> + ))} + +

Page not found

+ Return to the laboratory + + } + /> +
+
+
+ Rooted in curiosity. Grown with care. + +
+ + ); +} +createRoot(document.getElementById("root")!).render( + + + + + + + , +); diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css new file mode 100644 index 0000000..7ece704 --- /dev/null +++ b/apps/web/src/styles.css @@ -0,0 +1,299 @@ +@import "tailwindcss"; +:root { + font-family: Arial, sans-serif; + color: #20392d; + background: #f6f3e8; + font-synthesis: none; + --background: #f6f3e8; + --foreground: #20392d; + --primary: #32684a; + --border: #d8ded2; + --radius: 0.75rem; +} +* { + box-sizing: border-box; +} +body { + margin: 0; +} +a { + color: inherit; + text-decoration: none; +} +a:focus-visible { + outline: 3px solid #b76f52; + outline-offset: 5px; + border-radius: 3px; +} +header, +main, +footer { + max-width: 1280px; + margin: auto; +} +header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 29px 42px; + border-bottom: 1px solid #d8ded2; + gap: 25px; +} +.brand { + display: flex; + align-items: center; + gap: 12px; + font: + 23px Georgia, + serif; +} +.brand-icon { + display: grid; + place-items: center; + border: 1px solid #b3c1a7; + border-radius: 50%; + width: 46px; + height: 46px; +} +.brand small { + display: block; + font: + 9px Arial, + sans-serif; + letter-spacing: 2px; + margin-top: 7px; +} +nav { + display: flex; + gap: 27px; + font-size: 13px; +} +nav a { + padding: 13px 0; + color: #586c5e; +} +nav a.active { + color: #20392d; + border-bottom: 2px solid #32684a; +} +main { + padding: 0 42px 60px; +} +.hero { + display: grid; + grid-template-columns: 1.1fr 1fr; + align-items: center; + gap: 45px; + min-height: 530px; +} +.eyebrow { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 2.5px; + color: #647961; + font-weight: bold; + margin: 0 0 22px; +} +h1 { + font: + clamp(40px, 4.8vw, 67px)/1.07 Georgia, + serif; + letter-spacing: -2px; + max-width: 620px; + margin: 0 0 25px; +} +.description { + font-size: 15px; + line-height: 1.85; + color: #5d7062; + max-width: 440px; +} +.edition { + margin-top: 37px; + display: flex; + gap: 19px; + flex-wrap: wrap; + font-size: 9px; + letter-spacing: 1.5px; + color: #687665; +} +.edition span:first-child { + color: #9b6349; +} +figure { + margin: 0; + max-width: 440px; + justify-self: center; + width: 100%; +} +figure svg { + display: block; + width: 100%; + height: auto; +} +figcaption { + text-align: center; + font-size: 8px; + letter-spacing: 2px; + color: #75816f; + margin-top: -10px; +} +.workbench { + border-top: 1px solid #d8ded2; + padding-top: 28px; +} +.section-heading { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; +} +.section-heading .eyebrow { + margin-bottom: 8px; +} +h2 { + font: + 29px Georgia, + serif; + margin: 0; +} +.badge { + font-size: 10px; + color: #63775f; + background: #e9ecde; + border: 1px solid #d7ddc9; + padding: 9px 13px; + border-radius: 20px; + white-space: nowrap; +} +.empty { + margin-top: 23px; + padding: 36px 20px; + text-align: center; + background: #fffcf4; + border: 1px solid #d8ded2; + border-radius: 12px; +} +.icon { + display: inline-grid; + place-items: center; + width: 49px; + height: 49px; + background: #edf0e4; + border-radius: 50%; + color: #617e56; +} +h3 { + font: + 23px Georgia, + serif; + margin: 17px 0 10px; +} +.empty p { + max-width: 440px; + margin: 0 auto; + color: #637565; + font-size: 13px; + line-height: 1.8; +} +.text-link { + display: inline-flex; + align-items: center; + gap: 9px; + font-size: 12px; + margin-top: 21px; + color: #32684a; +} +footer { + display: flex; + justify-content: space-between; + gap: 20px; + border-top: 1px solid #d8ded2; + padding: 24px 42px; + font-size: 11px; + color: #6a7c70; +} +.health { + display: flex; + align-items: center; + gap: 8px; +} +.dot { + height: 6px; + width: 6px; + border-radius: 50%; + background: #b76f52; + flex-shrink: 0; +} +.online { + background: #547e4e; +} +.skip { + position: absolute; + top: -100px; + padding: 10px; + background: #fffcf4; +} +.skip:focus { + top: 5px; + left: 5px; +} +@media (max-width: 720px) { + header { + padding: 22px; + flex-direction: column; + align-items: flex-start; + gap: 16px; + } + nav { + width: 100%; + justify-content: space-between; + gap: 10px; + font-size: 12px; + } + main { + padding: 0 22px 35px; + } + .hero { + grid-template-columns: 1fr; + gap: 0; + padding-top: 38px; + } + .hero figure { + max-width: 320px; + margin: 12px 0 30px; + } + .eyebrow { + margin-bottom: 17px; + } + h1 { + font-size: 46px; + } + .description { + font-size: 14px; + } + .edition { + margin-top: 25px; + } + footer { + padding: 22px; + flex-direction: column; + gap: 12px; + } + .badge { + font-size: 9px; + padding: 8px; + } + .empty { + padding: 28px 16px; + } +} +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation: none !important; + transition: none !important; + scroll-behavior: auto !important; + } +} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 0000000..8a1afe6 --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "noEmit": true + }, + "include": ["src", "vite.config.ts"] +} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts new file mode 100644 index 0000000..8975de0 --- /dev/null +++ b/apps/web/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; +export default defineConfig({ + plugins: [react(), tailwindcss()], + server: { + proxy: { + "/health": "http://localhost:3000", + "/api": "http://localhost:3000", + }, + }, +}); diff --git a/artifacts/m0/1440.png b/artifacts/m0/1440.png new file mode 100644 index 0000000..ad6bb83 Binary files /dev/null and b/artifacts/m0/1440.png differ diff --git a/artifacts/m0/360.png b/artifacts/m0/360.png new file mode 100644 index 0000000..b862d05 Binary files /dev/null and b/artifacts/m0/360.png differ diff --git a/artifacts/m0/browser-checks.json b/artifacts/m0/browser-checks.json new file mode 100644 index 0000000..69e2d9c --- /dev/null +++ b/artifacts/m0/browser-checks.json @@ -0,0 +1,62 @@ +{ + "results": [ + { + "width": 360, + "route": "/", + "url": "http://localhost:8155/lab", + "viewport": 360, + "document": 360 + }, + { + "width": 360, + "route": "/breed", + "url": "http://localhost:8155/breed", + "viewport": 360, + "document": 360 + }, + { + "width": 360, + "route": "/gallery", + "url": "http://localhost:8155/gallery", + "viewport": 360, + "document": 360 + }, + { + "width": 360, + "route": "/about", + "url": "http://localhost:8155/about", + "viewport": 360, + "document": 360 + }, + { + "width": 1440, + "route": "/", + "url": "http://localhost:8155/lab", + "viewport": 1440, + "document": 1440 + }, + { + "width": 1440, + "route": "/breed", + "url": "http://localhost:8155/breed", + "viewport": 1440, + "document": 1440 + }, + { + "width": 1440, + "route": "/gallery", + "url": "http://localhost:8155/gallery", + "viewport": 1440, + "document": 1440 + }, + { + "width": 1440, + "route": "/about", + "url": "http://localhost:8155/about", + "viewport": 1440, + "document": 1440 + } + ], + "errors": [], + "keyboard": "Skip link is first focus target" +} \ No newline at end of file diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 0000000..2bcb530 --- /dev/null +++ b/assets/README.md @@ -0,0 +1,2 @@ +# Artwork +The botanical shell study in apps/web/src/main.tsx is original SVG artwork created for Botanica by Codex (2026-10-28), provided under the repository terms. No external assets are used. This is decorative M0 art; species primitives and the genotype renderer belong to M2. diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..16bd6a3 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,71 @@ +name: botanica +services: + web: + build: + context: . + dockerfile: apps/web/Dockerfile + ports: ["127.0.0.1:${HOST_PORT:-8155}:8080"] + depends_on: + api: + condition: service_healthy + restart: unless-stopped + networks: [internal] + api: + build: + context: . + dockerfile: apps/server/Dockerfile + environment: + NODE_ENV: production + PORT: "3000" + HOST_PORT: ${HOST_PORT:-8155} + DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} + PUBLIC_DOMAIN: ${PUBLIC_DOMAIN:-http://localhost} + depends_on: + bootstrap: + condition: service_completed_successfully + db: + condition: service_healthy + healthcheck: + test: + [ + "CMD", + "node", + "-e", + "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))", + ] + interval: 10s + timeout: 3s + retries: 10 + start_period: 20s + restart: unless-stopped + networks: [internal] + bootstrap: + build: + context: . + dockerfile: apps/server/Dockerfile + environment: + DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} + command: ["sh", "-c", "node dist/migrate.js && node dist/seed.js"] + depends_on: + db: + condition: service_healthy + restart: "no" + networks: [internal] + db: + image: postgres:17-alpine + environment: + POSTGRES_DB: ${POSTGRES_DB:-botanica} + POSTGRES_USER: ${POSTGRES_USER:-botanica} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + volumes: ["postgres_data:/var/lib/postgresql/data"] + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 5s + timeout: 5s + retries: 15 + restart: unless-stopped + networks: [internal] +networks: + internal: +volumes: + postgres_data: diff --git a/docs/API_AND_DATA.md b/docs/API_AND_DATA.md new file mode 100644 index 0000000..9f3c903 --- /dev/null +++ b/docs/API_AND_DATA.md @@ -0,0 +1,53 @@ +# API and persistence contracts + +## API conventions +Versioned REST at `/api/v1`. JSON UTF-8. Zod validation for input. Consistent failures `{error:{code,message,details?}}`; use 400 for malformed, 404 for missing, 409 for incompatible/duplicate, 500 for unexpected failures. `GET /health` returns 200 JSON and DB health information without credentials. + +## Endpoints M0–M4 +- `GET /health` — proxy-through health. +- `GET /api/v1/catalog/species` — available species and allele catalogs. +- `POST /api/v1/phenotype/resolve` — validated genome → phenotype + rarity preview; no persistence. +- `POST /api/v1/preview` — validated genome + seed + stage → serialized SVG (or typed scene; choose one and document). +- `GET /api/v1/plants?cursor=&limit=&species=&rarity=&pigment=` — paginated saved demo plants. +- `POST /api/v1/plants` — create demo plant using server-side validation and secure initial visual seed if omitted; no privilege to set own immutable rarity/parent claims. +- `GET /api/v1/plants/:id` — plant, phenotype, parent IDs and status. +- `POST /api/v1/breeding` — `{parentAId,parentBId,idempotencyKey}`; server validates, generates seed, applies genetics, persists child + event in a single transaction. Repeated key returns same result (not additional child). +- `GET /api/v1/breeding?limit=&cursor=` — recent events. + +## Data model (first migration) +`species(id PK, name, compatibility_group, catalog_version, config_jsonb)`. +`players(id UUID PK, display_name, created_at)` — seed single sandbox player only; no auth yet. +`plants(id UUID PK, owner_id FK, species_id FK, genome_version integer, genome jsonb, mutations jsonb, visual_seed integer, renderer_version integer, rarity_policy_version integer, rarity_score integer, rarity_tier text, generation integer, parent_a_id nullable FK self, parent_b_id nullable FK self, created_at timestamptz)`. +`breeding_events(id UUID PK, owner_id FK, parent_a_id FK, parent_b_id FK, child_id UNIQUE FK, rng_seed integer, engine_version integer, mutation_events jsonb, idempotency_key unique text, created_at timestamptz)`. +`discoveries(player_id FK, signature text, first_discovered_at timestamptz, PK(player_id,signature))` optional in M4. + +Indices: plants(owner_id,created_at DESC), plants(species_id,rarity_tier), breeding_events(owner_id,created_at DESC). Use enum validation in domain layer. Schema-specific migrations must be checked into repository. Seed catalog + demo player in idempotent script; sample gallery generated from seeded fixed seeds for repeatability. + +## Example valid genome +```json +{ + "pigment": ["P", "G"], + "leafShape": ["L", "O"], + "leafPattern": ["V", "S"], + "growthHabit": ["B", "C"], + "flowerColor": ["R", "W"], + "flowerDensity": ["H", "M"] +} +``` + +## Example breeding request +```json +{ + "parentAId": "uuid-parent-a", + "parentBId": "uuid-parent-b", + "idempotencyKey": "uuid-user-generated-unique-request" +} +``` + +## Security and transaction invariants +- Only exposed service is the web reverse proxy. Proxy `/api/` and `/health` to `api:3000` over Compose internal network, not to `localhost` from inside container. +- Demo endpoints are deliberately not publicly secure without authentication. For internet deployment, **must** add auth + ownership and rate limits first; current MVP intended for localhost only. +- Sanitize SVG input by using known primitive IDs/validated hex palette values; never interpolate arbitrary string into SVG markup. +- No raw SQL concatenation; no secrets in frontend build; limit payload size. +- Idempotent breeding ensures retries cannot create duplicated child plants. +- API response and schema types must be shared where sensible; avoid circular import chains. diff --git a/docs/CODEX_START_PROMPT.md b/docs/CODEX_START_PROMPT.md new file mode 100644 index 0000000..3c87ae4 --- /dev/null +++ b/docs/CODEX_START_PROMPT.md @@ -0,0 +1,12 @@ +# First message to Codex + +Read `AGENTS.md` and the whole `docs/` handoff. You are implementing a real repository, not writing another plan. Begin with **milestone M0 only** from `docs/IMPLEMENTATION_PLAN.md`: scaffold the workspaces, create all Dockerfiles, web reverse proxy and a responsive Plant Laboratory shell with a working API and Postgres migrations. Keep the requested `.env` values (`HOST_PORT=8155`, `PUBLIC_DOMAIN=http://localhost`). Do not add unnecessary frameworks. Execute the M0 acceptance commands, fix failures, update `docs/PROGRESS.md`, then report results and ask to proceed to M1. All later milestones must follow the exact order and contracts specified. + +# Subsequent prompts + +- `Implement M1 exactly as specified, validate it, update PROGRESS.md, and report.` +- `Implement M2 exactly as specified, validate it, update PROGRESS.md, and report.` +- `Implement M3 exactly as specified, validate it, update PROGRESS.md, and report.` +- `Implement M4 exactly as specified, validate it, update PROGRESS.md, and report.` + +For more independent execution: `Implement M0 through M4 sequentially, checking each acceptance gate and recording verified results. Do not skip failed gates.` diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md new file mode 100644 index 0000000..ddf94d8 --- /dev/null +++ b/docs/DECISIONS.md @@ -0,0 +1,12 @@ +# Architectural decisions (fixed for v1) + +- First deliverable is Plant Laboratory, not the entire idle economy. +- React/Vite frontend; Fastify backend; PostgreSQL; npm workspaces; Docker Compose; TypeScript across code. +- Illustrated 2D original SVG primitives; pure deterministic renderer with optional PixiJS **later** only if performance demands. +- Diploid fixed-locus genetics; explicit seed and versions; no bio-realism claims. +- One demonstrator species (`velvetleaf`), other species supported by schema but not required before M4. +- Two-parent controlled breeding, server authoritative and idempotent. +- Single sandbox demo player, no auth or public release at MVP stage. +- English UI for v1, sensible localization-ready keys later. +- Simple focused tests over test-heavy process; acceptance gates must work. +- No cloud API dependencies for game operation. diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..6bf26c4 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,44 @@ +# Docker Compose deployment/runbook + +## Requested configuration +`.env` includes exactly: +``` +HOST_PORT=8155 +PUBLIC_DOMAIN=http://localhost +``` +The actual **browser URL** is `http://localhost:8155`. `PUBLIC_DOMAIN` is the domain origin requested by owner and deliberately has no port. For local URL construction derive `PUBLIC_BASE_URL=http://localhost:8155`; prefer relative API URLs (`/api/v1/...`) on the web client. Do not send client requests directly to container name `api` (not browser-resolvable). + +## M0 required deliverables +- `apps/web/Dockerfile`: multi-stage Node build → lightweight nginx runtime, serving Vite output and forwarding `/api/`, `/health` to `api:3000`. Create nginx config under `apps/web/nginx.conf`. Use SPA history fallback to `index.html`; ensure `/health` and `/api/` proxy rules take precedence. +- `apps/server/Dockerfile`: multi-stage Node build, Node runtime listening on `0.0.0.0:3000` and health endpoint. +- `compose.yaml` is provided; adapt only if necessary for a **working**, secure Compose setup, preserving exposed host port, private DB/API, healthcheck and volume. If Node native dependencies require extra Docker libraries, document why. +- Use `NODE_ENV=development` only for initial local setup; deployment builds should run production assets and API with runtime environment. +- Implement Drizzle migrations on container initialization as a controlled documented command, not an uncontrolled migration loop. Provide `npm run db:migrate`, `npm run db:seed`. +- `docker compose config` should parse correctly before Docker build. + +## Commands +```bash +# local bootstrap (after M0 files created) +docker compose config +docker compose up --build -d +docker compose ps +curl -fsS http://localhost:8155/health + +# Stop (keep database) +docker compose down + +# NEVER use -v unless intentionally deleting data +docker compose down -v +``` + +## Critical security note +The starter `.env` has a deliberately weak local-only placeholder password. Generate a strong password before exposing the app or sharing `.env`. `.env` is gitignored. In this MVP there is no user authentication: **do not open the app to the public internet** until auth/access control and API throttling are implemented. + +## Future reverse proxy +Later replace `PUBLIC_DOMAIN` with a true HTTPS origin and configure TLS at the reverse proxy; no fixed 8155 should be appended for an external HTTPS origin. Keep Compose `HOST_PORT` configurable; enable trusted proxy headers only for known proxy. Do not overbuild this now. + +## Implemented M0 bootstrap + +Compose publishes web on loopback (`127.0.0.1:${HOST_PORT}:8080`), with nginx running as an unprivileged user. API and DB have no published ports. The `bootstrap` one-shot service uses the API image to run `node dist/migrate.js` then `node dist/seed.js`; API waits for successful completion. A PostgreSQL transaction advisory lock serializes migration execution. A migration tracking table prevents repeat schema application. Seeds use conflict-safe inserts and preserve existing records. + +For explicit reruns use `docker compose run --rm bootstrap node dist/migrate.js` or `docker compose run --rm bootstrap node dist/seed.js`. Runtime images contain compiled JavaScript; root npm migration/seed scripts use TypeScript tooling for host development with an explicit `DATABASE_URL`. Compose production mode overrides the starter development value without modifying `.env`. diff --git a/docs/GENETICS_SPEC.md b/docs/GENETICS_SPEC.md new file mode 100644 index 0000000..ac913dc --- /dev/null +++ b/docs/GENETICS_SPEC.md @@ -0,0 +1,59 @@ +# Genetics contract v1 + +## Basic representation +This is an intentional **game model** inspired by Mendelian genetics. It is not rigorous botanical genetics. Genomes are diploid: each locus is `[alleleFromParentA, alleleFromParentB]` (ordering retained for parental origin if useful). The trait catalog is species-family scoped, versioned and data-driven. + +Initial loci: +| key | alleles | mode | visible phenotype | +|---|---|---|---| +| pigment | `G,P,R,Y` | categorical dominance `P > R > Y > G` for v1 | leaf base palette | +| leafShape | `O,L,S` | `L > S > O` | oval/lobed/pointed silhouette | +| leafPattern | `S,V,M` | `V > M > S` | solid/variegated/marbled | +| growthHabit | `C,B,T` | `T > B > C` | compact/bushy/trailing | +| flowerColor | `W,P,B,R` | `B > R > P > W` | flower palette | +| flowerDensity | `L,M,H` | `H > M > L` | number of blooms | + +Dominance order is a gameplay simplification and is **not** an assertion about real plant biology. Use stable, named IDs in catalog; do not rely on one-letter characters to be globally unique between loci. + +## Determinism contract +- `resolvePhenotype(speciesId, genome, mutations, catalogVersion): Phenotype` pure, no RNG. +- `layoutPlant(speciesId, phenotype, visualSeed, growthStage, rendererVersion): PlantScene` pure; same inputs result in same scene bytes/geometry to meaningful precision. +- `breed(parentA, parentB, seed, catalogVersion): BreedingResult` pure; reject incompatible species, select one parental allele per locus with uniform seeded draw, independently across loci for v1. Use fixed PRNG and documented serialization of seed. Save source seed, engine version and result in breeding event. +- `generateSeed()` server-only with secure randomness; genetics gets explicit numerical seed. Use a testable seeded generator such as Mulberry32 or PCG32 and document cross-runtime integer semantics. +- `visualSeed` affects only cosmetic leaf placement/density jitter within the constraints of resolved phenotype. Never affects hereditary traits. +- Allele catalog/engine version changes must not silently rewrite existing stored genotypes. Keep backwards-compatible resolution or migration scripts. + +## Cross compatibility +- Species have `compatibilityGroup`, plus optional allowed partner overrides. +- Default: only same-group species can cross. Unapproved combinations reject with machine code `INCOMPATIBLE_SPECIES`. +- In v1, seed offspring species is parent A if same species, otherwise use a **specified deterministic hybrid rule**; initially disallow interspecies even within group until hybrids are defined in data. Code should have a clear compatibility extension point. + +## Mutation rules +- Per-locus mutation is optional and small; **baseline probability 0.005 per inherited allele** in configurable catalog; when triggered replace inherited allele with a *different valid allele* selected uniformly. Persist event log of mutations. +- Rare named morphological mutation `golden_veins` is an explicit modifier with a separate configurable triggered probability, initially **0.001 per offspring**, rolled only at breeding. It changes vein rendering only, not chromosome alleles. +- These are example balance values, not promises of rarity tiers; capture actual result via tests. +- Mutation outcome must be committed once with breeding transaction, not recomputed on fetching plant. + +## Rarity policy +- Rarity is a **derived, versioned classification**, not an independent RNG reward. +- Score `0..100`: +20 for each recessive unusual phenotype (max +40), +20 for variegated/marbled patterns, +25 for named rare mutation, +15 for other uncommon combination; clamp 100. Define unusual traits per species catalog rather than hardcoding across all species. +- Tier thresholds: Common 0–9, Uncommon 10–24, Rare 25–44, Epic 45–64, Legendary 65–84, Mythic 85–100. Some tiers may be inaccessible before more traits/species are added; do not misrepresent tier frequency. +- `rarityPolicyVersion=1`, store score and tier at birth with policy version; provide a recompute command for future version upgrades, but no automatic silent rewrite. +- Display collection frequency (from actual dataset) separately from rarity tier. + +## Breeding algorithm outline +1. Validate species compatibility, complete genomes and locus alleles using Zod. +2. Instantiate deterministic RNG from event seed. +3. At each locus, draw exactly one of each parent's two alleles; invoke mutation checks in **stable sorted locus order**. +4. Roll named mutations in stable sorted ID order (fixed order documented). +5. Resolve phenotype and classify rarity. +6. Derive fresh child `visualSeed` deterministically from event seed; persist child, generation `max(parents)+1`, parent IDs, breed event and costs atomically. +7. Return resolved phenotype and persisted child. + +## Tests (mandatory, small and meaningful) +- `Pg × Pg` in a single dominant locus: 25% homozygous dominant, 50% heterozygous, 25% homozygous recessive after >=10,000 draws with mutation OFF, tolerances ±2 percentage points. +- Two parents with same genotype and fixed seed always reproduce same child. +- Same genome, different visualSeed: same phenotype, different cosmetics. +- Invalid alleles, missing loci and incompatible species fail. +- Mutations never create an unknown allele and are logged. +- No simultaneous duplicate breeding can spend resources twice (when economy implemented); meanwhile transaction persists exactly one child per idempotency key. diff --git a/docs/GRAPHICS_SPEC.md b/docs/GRAPHICS_SPEC.md new file mode 100644 index 0000000..45bb950 --- /dev/null +++ b/docs/GRAPHICS_SPEC.md @@ -0,0 +1,49 @@ +# Graphics and renderer architecture v1 + +## Principle +No pre-rendered combination explosion. Build plants from **illustrated hand-authored SVG primitives** composed into a deterministic scene. Species style is recognizable: no generic random leaves substituting for all botanical families. + +## Module shape +- `packages/renderer/src/layout.ts`: pure scene generation (geometry, layer ordering, deterministic placements). +- `packages/renderer/src/renderSvg.ts`: scene → **safe deterministic SVG string** with validated colors/asset paths, fixed viewBox `0 0 512 512`, no scripts/external href. +- `apps/web/src/components/PlantPreview.tsx`: SVG display wrapper; supports 64, 128, 512 pixel responsive presentation. +- Exports: browser SVG native; PNG via canvas using locally generated serialized SVG with same-origin/Blob URLs and graceful error; JSON genome export separate. + +## Render stack, back to front +1. soft ground shadow; 2. pot rear; 3. rear leaves; 4. stem branches; 5. front leaves; 6. blooms/inflorescence; 7. pigment patterns + veins; 8. pot lip and highlights. + +## Primitive asset contract +SVG primitives saved under `assets/species//` as `.svg` source and optional typed path definitions. Each primitive declares: +``` +{ + "assetId": "velvetleaf/leaf_lobed_v1", + "category": "leaf", + "anchor": [0.5, 0.92], + "bounds": [0, 0, 100, 140], + "paletteSlots": ["leafBase", "leafShade", "vein", "variegation"], + "rendererVersion": 1 +} +``` +Each path has canonical coordinates and must be paintable using declared palette slots. No bitmap/image elements, filter-heavy effects, third-party assets, arbitrary scripts or CSS foreignObject. + +## v1 authored assets +- Species `velvetleaf`: 3 silhouettes (oval, lobed, pointed), each with veins and highlights. +- 3 branch templates (compact, bushy, trailing) and stem primitive. +- 4 bloom variants / petals, 2 pots, ground shadow. +- 4 growth stages: seedling, juvenile, mature, flowering. Bloom graphics hidden until flowering stage. Stage affects geometry/counts, never genotype. +- 6 curated palettes, mapping allele phenotype to leaf colors. Variegation and marbling are **masks or path overlays**, not arbitrary giant color rectangles. +- Golden veins mutation renders as restrained warm-gold vein overlay. + +## Scene and repeatability +- Layout output is a data tree: layer, assetId, x, y, rotation, scale, palette map, optional clipping/masking config. +- Use seeded RNG for branch/leaf placement, but do not randomize core silhouette beyond recognition. +- Bounds must never clip major leaves or pot. Maintain consistent scale across gallery cards. +- Render to SVG with stable ID namespace per preview (no collisions when multiple plants appear on the page). IDs derived from scene instance + fixed index; do not affect visual deterministic geometry. +- Avoid React rerender of all 100 plants when changing one editor control; memoize based on canonical render inputs. + +## Acceptance visual QA +- At least 12 intentional variants (3 leaf shapes × 2 pigments × 2 patterns) clearly distinguishable; manually visually inspect using rendered browser screenshot. +- Large preview remains attractive at 512px, readable at 64px; avoid jagged joins and disproportionate pots. +- 100 small previews render without unacceptable jank on a normal desktop browser. +- Exported SVG opens in a standard SVG viewer without external assets; PNG export has correct alpha/background as selected. +- All graphics are original vector assets stored in the repository; document creator and licensing in `assets/README.md`. diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..7edb98f --- /dev/null +++ b/docs/IMPLEMENTATION_PLAN.md @@ -0,0 +1,89 @@ +# Implementation plan — explicit Codex execution gates + +## Working rules +Complete milestones sequentially. Finish one and validate it before moving on. Use `docs/PROGRESS.md` as execution log. Do not implement speculative game systems. In the event of a spec conflict, prioritize: `AGENTS.md`, then this plan, then narrower contracts. Document deviations instead of silently redefining requirements. Prefer small components, one implementation per responsibility and clear error states. + +## M0 — infrastructure & bootable application +**Objective:** real running web + API + database through Docker Compose, before domain features. + +Tasks: +1. Initialize npm workspace root with `apps/web`, `apps/server`, `packages/genetics`, `packages/renderer`, `packages/rules`, `packages/shared`. Configure shared TS config, ESLint if straightforward, testing via Vitest and root scripts `dev`, `build`, `typecheck`, `test`, `db:migrate`, `db:seed`. +2. Scaffold React/Vite + router + Tailwind, install shadcn/ui prerequisites only (add actual components as needed), Zustand and TanStack Query; a proper illustrated shell/header and top nav. Implement `/lab`, `/breed`, `/gallery`, `/about`; all pages may show clearly labeled empty states in M0. +3. Scaffold Fastify TypeScript API, Zod, Drizzle PostgreSQL connection, `/health` returning 200 if DB query succeeds. Implement uniform API errors. +4. Create DB schema and initial migration consistent with `docs/API_AND_DATA.md`, demo player + `velvetleaf` catalog seed; startup/bootstrap behavior must be repeatable. Seed operation idempotent. +5. Create production-quality Dockerfiles, nginx SPA/proxy config, .dockerignore. Make Compose from root work with provided .env (host 8155, public origin http://localhost). +6. Add simple README scripts, screenshots not mandatory yet. No credentials in Git. + +Gate: +- `docker compose config` works. +- `docker compose up --build -d` produces healthy API/DB and reachable UI at `http://localhost:8155`. +- `curl -fsS http://localhost:8155/health` returns HTTP 200 JSON. +- Mobile page renders without horizontal overflow. +- `npm run typecheck`, `npm run test`, `npm run build` pass (root tooling installed and lockfile committed). +- Database survives `docker compose down` / `up` and cannot be reached via published Compose ports. + +## M1 — genetics domain (pure package) +Tasks: +1. Create versioned catalogs/types, Zod validators, `validateGenome`, `resolvePhenotype`, `canBreed`, `breed`, `classifyRarity` as specified. +2. Implement a deterministic seeded PRNG with documented integer rules and deterministic cross-run results. Do not mix RNG into renderer. +3. Allele inheritance: 50/50 from each parent independently by locus; dominance policy per locus; named mutation logging; explicit exceptions for invalid/incompatible pairs. +4. Add proper unit tests on example cross, mutation boundaries, deterministic replay, phenotypes and rarity policy. +5. Implement `/api/v1/catalog/species` and `/api/v1/phenotype/resolve` with shared DTOs. + +Gate: +- Example cross with mutations disabled yields 1:2:1 genotypes over 10k runs within ±2 percentage points. +- Same seed → exact same result, across calls. +- Cross-species breeding blocked unless compatibility is explicitly defined. +- Untrusted client alleles rejected by server. +- `npm run test`, `typecheck`, `build` pass. + +## M2 — illustrated vector rendering +Tasks: +1. Create at least **original** handcrafted SVG primitive sources (see GRAPHICS_SPEC). Strong visual quality; use multiple paths, veins/shading, artistically composed plant silhouette. +2. Implement species-specific plant grammar and pure geometry layout with visualSeed, phenotype, 4 stages and stable IDs. +3. Render an isolated viewBox 512 SVG. Apply color palettes, silhouette switching, variegation/marbling and golden veins mutation. No unsafe injected XML. +4. Add export SVG and PNG with transparent or warm background selection, ensure rendered result matches preview. +5. Add gallery proof view showing at least 12 systematic variations and 100-seed smoke demo; perform browser visual QA and optimize if needed. + +Gate: +- 3 leaf shapes, 2 pigments, 2 patterns visibly distinct. +- Same args produce same SVG geometry; same genotype with different visualSeed varies only cosmetically. +- 64px icon and 512px hero remain legible; no obvious clipping. +- SVG and PNG exports open and visually match. +- 100 plants render without noticeable frozen UI on typical desktop. +- Build/tests/typecheck pass. + +## M3 — editor and persistent breeding +Tasks: +1. Build polished Lab: live plant preview, species selector, diploid allele editor, stage selector, visual seed control and mutation toggles (manual preview only, not falsely claimed as bred mutations), genotype/phenotype and score explanations. +2. Add server-side `POST /api/v1/plants`, `GET /api/v1/plants/:id`, pagination GET. Persist canonical genomes, seeds, lineage fields. Seed demo parents. +3. Implement server authoritative `POST /api/v1/breeding` idempotent and transactional, plus history endpoint; create real child instance with securely selected event seed and recorded parent IDs. +4. Implement Breeding Lab screen: parent selection, validation, cross, result card and lineage navigation. Show accurate example probabilities only for single-gene preview. +5. Mutation logic and rarity scores computed once at child creation and persisted. Data migration scripts tested on clean DB. + +Gate: +- Editing allele changes expected visible traits instantly. +- Breed yields child with exactly two persisted parents and generation +1. +- Retry same idempotencyKey returns same child, no duplicates. +- Cross incompatible species refuses with clear message. +- Browser reload persists generated plants. +- No server error leaks secrets. Tests/build/typecheck pass. + +## M4 — collection gallery and hardening +Tasks: +1. Implement `/gallery` cards with filters, sorting and pagination, details inspector, lineage ancestry links and export JSON/SVG/PNG. +2. Create **100 deterministic varied demo plants** using seed script. Make seed idempotent so running twice does not duplicate. +3. Add helpful empty/loading/error states, toast/action confirmation, responsive layout and focus handling, reduced motion. +4. Polish botanical art direction and cards, ensure readability at 64px; inspect screenshots for desktop + mobile and fix visual issues. +5. Document API via concise examples, deployment constraints, future idle-game roadmap; verify no public auth claims or security oversell. + +Gate: +- 100 plant gallery searchable/filterable without excessive UI lag. +- Exported SVG, PNG and JSON valid; opens without external resources. +- Lineage from bred child to parents navigable. +- 360px and 1440px layouts verified. +- `docker compose up --build -d`, health, tests, typecheck, build pass. +- Project ready for further iteration, **not** publicly launchable until auth/ownership controls added. + +## Scope of next phase (do not code yet) +M5+ greenhouse economy (pots, seeds, currencies), offline time progression via event timestamps, upgrades and automation, seed shop, collection achievements, research, player accounts, secure multiplayer/trading. Should be planned after Plant Lab proof is accepted, not smuggled into MVP. diff --git a/docs/PRODUCT_SPEC.md b/docs/PRODUCT_SPEC.md new file mode 100644 index 0000000..9f5cdf7 --- /dev/null +++ b/docs/PRODUCT_SPEC.md @@ -0,0 +1,40 @@ +# Product specification — Plant Laboratory v1 + +## Goal +A functioning botanical breeding sandbox with beautiful **illustrated 2D** assets. This is the first slice of a larger idle plant collection/greenhouse game, not a generic admin UI. Initial interface language English. + +## Personas and core flow +1. Visitor opens responsive browser app at `/` and sees a lovely large plant rendered as SVG. +2. They choose species, edit diploid alleles, watch the plant change immediately, select a growth stage and set the cosmetic visual seed. +3. They select two parent plants and request breeding through the API; resulting child genotype and traceable parent IDs are saved by the server. +4. They browse a 100-plant visual gallery with filter by species, pigment, pattern and rarity, open details, inspect ancestry and export JSON/SVG/PNG. +5. At end of MVP, single demo player has local-account-free sandbox persistence; no login. Build ownership into schema for later authenticated accounts. Avoid exposing a destructive global reset in production. + +## Pages/screens +- `/lab`: hero live SVG preview (large), genome editor (editable locus/allele pairs), controls for stage, visualSeed, mutation flags, responsive inspector with phenotype and derived rarity. +- `/breed`: two parent pickers (from persisted sample plants), compatibility indicator, breeding button, outcome card, parent links, historic breed events. +- `/gallery`: responsive 2–5 column cards, virtualize or paginate when >100, species/trait/rarity filters, details sheet, download actions. +- `/about`: clear description of educational *fictional game genetics*, not a scientifically accurate botany simulator. +- `/` redirects to `/lab`. + +## Visual design +Mood: calm botanical cabinet of curiosities; credible handmade illustration rather than glossy tech dashboard. Canvas warm ivory, forest green controls, restrained terra-cotta, rare colors as accents. Dark mode optional, not prerequisite. Leaves and flowers must have deliberate illustrated shapes (veins, subtle layered shading); avoid simple emoji or generic circles. + +Design tokens suggested: +- Background `#F6F3E8`; surface `#FFFCF4`; ink `#20392D`; muted `#6A7C70`. +- Primary `#32684A`; secondary `#B76F52`; borders `#D8DED2`. +- Rarity accents: Common `#78977B`, Uncommon `#4F9D91`, Rare `#5489C6`, Epic `#9366C3`, Legendary `#CB9C39`, Mythic `#C76D87`. +- Use consistent typography (system serif for botanical headings, sans for controls); avoid remote font dependencies. + +## Core UX acceptance +- Clear keyboard-focus indication, semantic controls, labels, WCAG AA targets where feasible. +- Mobile 360px width does not overflow. Desktop 1440px has split inspector/gallery composition. +- SVG shapes readable at 64px card and at 500px inspector. +- Select altered allele → phenotype and illustration change without page refresh, for traits with visible manifestations. +- Controls show genotype **and** phenotype separately. +- Show why crossing is blocked if species incompatible. +- Show generated probabilities only if accurate to implemented model; do not imply independent rarity dice rolls. +- Reduced-motion users do not get excessive animations. No external runtime artwork APIs. + +## Explicitly out of scope for this handoff +Accounts/auth, inventory purchase/harvest economy, greenhouse automation/timers, social trading, realtime multiplayer, paywalls, AI-generated graphics, crop disease. Add later in separate plan after Laboratory MVP. diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md new file mode 100644 index 0000000..a6441eb --- /dev/null +++ b/docs/PROGRESS.md @@ -0,0 +1,40 @@ +# Implementation progress + +- [x] M0 — workspaces, Compose, healthcheck, migrations, responsive shell. +- [ ] M1 — genetics contracts, deterministic PRNG, pure engine + tests. +- [ ] M2 — procedural illustrated SVG renderer, exports, visual QA. +- [ ] M3 — lab editor, server persistence, breeding flow. +- [ ] M4 — gallery, provenance, UX polish, full acceptance. + +## M0 — 2026-10-28 + +Implemented npm workspaces (`apps/web`, `apps/server`, and four pure package boundaries), shared strict TypeScript configuration, Vitest, and root development/build/typecheck/test/migration/seed commands. Installed compatible dependencies and generated `package-lock.json`. + +Added React/Vite/Tailwind shell with router, TanStack Query health indicator, Zustand and shadcn prerequisites. Four routes have explicit M0 empty states, keyboard focus and skip navigation, responsive layout and reduced-motion support. Original static botanical SVG is shell decoration; no genetics or procedural renderer is claimed at M0. + +Added Fastify API with uniform safe errors, DB-dependent `/health`, Zod runtime configuration, local/reverse-proxy public URL derivation, PostgreSQL pool and Drizzle schema. Checked-in initial SQL migration creates species, players, plants and breeding events with ownership/lineage references, uniqueness, indexes and basic data constraints. Transaction-locked migration tracking and idempotent catalog/player seeding run in a one-shot Compose bootstrap service before API startup. + +Added non-root production Docker images, nginx history fallback and API/health reverse proxy, `.dockerignore`, local-only web publication and persistent PostgreSQL volume. Updated README and asset provenance. `.env` remains unchanged and ignored; requested values are `HOST_PORT=8155`, `PUBLIC_DOMAIN=http://localhost`. + +### Verified acceptance + +- `docker compose config --quiet`: passed. +- `docker compose up --build -d`: passed; API and PostgreSQL healthy; bootstrap exits successfully. +- `docker compose ps`: API/DB running healthy; web running at `127.0.0.1:8155`. +- `curl.exe -fsS http://localhost:8155/health`: HTTP 200, `{"status":"ok","database":"connected"}` through nginx. +- `npm run typecheck`: passed across all six workspaces. +- `npm run test`: 3 tests passed (DB health success, safe failure/404, local versus reverse-proxy URL configuration). +- `npm run build`: all workspaces passed; Vite generated production assets. +- Repeated `docker compose run --rm bootstrap node dist/migrate.js` and `node dist/seed.js`: passed; counts remain one player, one species, one migration. +- `docker compose down` then `docker compose up -d --wait`: passed without removing volumes. Player UUID and original `created_at` remained identical; velvetleaf catalog persisted. +- Docker inspection reports empty published port bindings for API and DB. Only web publishes a host port; it binds to loopback. +- Headless Chrome visited all four routes at 360px and 1440px, with zero JavaScript page errors and document width equal to viewport width. `/` redirects to `/lab`. Desktop and mobile screenshots visually inspected; see `artifacts/m0/1440.png`, `artifacts/m0/360.png`, and `artifacts/m0/browser-checks.json`. +- Git ignore verification confirms `.env` excluded; no live credentials in tracked source. + +### Execution notes and limits + +Initial Docker build started before npm finished writing the lockfile and failed; rebuilding with the completed lockfile succeeded. Initial typecheck found Fastify's unknown error type; handler now narrows it safely. Vitest's temporary-cache writes were blocked by the Windows sandbox; rerunning with approved expanded execution passed. Docker, npm network access and headless Chrome also required expanded execution in this environment. + +No M1–M4 behavior is implemented. Domain packages intentionally establish empty module boundaries until their milestone. No ESLint was added; strict TypeScript and focused tests provide the M0 checks. This remains a local demo without authentication. + +M0 gate complete. Stop here. Next: M1 genetics contracts, deterministic PRNG, inheritance engine and tests, after user instruction to proceed. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..91c0864 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3894 @@ +{ + "name": "botanica", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "botanica", + "version": "0.0.0", + "workspaces": [ + "apps/*", + "packages/*" + ], + "devDependencies": { + "@types/node": "^24.0.0", + "concurrently": "^9.2.1", + "typescript": "~5.9.3", + "vitest": "^4.1.0" + } + }, + "apps/server": { + "name": "@botanica/server", + "version": "0.0.0", + "dependencies": { + "drizzle-orm": "^0.45.4", + "fastify": "^5.12.7", + "pg": "^8.16.3", + "zod": "^4.0.0" + }, + "devDependencies": { + "@types/pg": "^8.15.5", + "tsx": "^4.20.0" + } + }, + "apps/web": { + "name": "@botanica/web", + "version": "0.0.0", + "dependencies": { + "@tanstack/react-query": "^5.0.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.577.0", + "react": "^19.3.1", + "react-dom": "^19.3.1", + "react-router": "^7.0.0", + "tailwind-merge": "^3.0.0", + "zustand": "^5.0.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^6.0.0", + "tailwindcss": "^4.0.0", + "vite": "^8.3.5" + } + }, + "node_modules/@botanica/genetics": { + "resolved": "packages/genetics", + "link": true + }, + "node_modules/@botanica/renderer": { + "resolved": "packages/renderer", + "link": true + }, + "node_modules/@botanica/rules": { + "resolved": "packages/rules", + "link": true + }, + "node_modules/@botanica/server": { + "resolved": "apps/server", + "link": true + }, + "node_modules/@botanica/shared": { + "resolved": "packages/shared", + "link": true + }, + "node_modules/@botanica/web": { + "resolved": "apps/web", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.7.tgz", + "integrity": "sha512-VP0yIfwlc1ohj733keYDqyHm1VWeP9VQxYomunhr90jkC4aNNibbkrRiaNgPgqj1ORxUenILR4qzA/ppYBNAtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^4.0.0" + } + }, + "node_modules/@fastify/deepmerge": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-3.2.4.tgz", + "integrity": "sha512-kc8GQq5gX9RRhw0rov3DMrN8wS8yRlxJRdDzsYwwdBV/wazuVgc/1K7CKL90tlvu3RlHan6Y0R0aE3nJtBz2eQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/error": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.1.tgz", + "integrity": "sha512-MYBumuIwP+5MIpXSyKm6/o1Eigcoij186lWUssX3LH5U8KWqU0ktZhzC4CErIX9EJeQeRGI+zJZQWakBlFbUcg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.1.1.tgz", + "integrity": "sha512-pyxRKk7UpUnnrztP9pF4+ZYxusEQ4LifTyzRdiXmyql1QezNHxO02D25MVU2o4kMeAMYWAjG6RKRlojgkbiDAw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^7.0.0" + } + }, + "node_modules/@fastify/forwarded": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.1.2.tgz", + "integrity": "sha512-89WiRG+hW3VQSfCwg4Hb8b43PeyEX15K5+lEfsZ4EUth/FyOjS/lv8HsVuswUNXWiSnxMGNouE/fq/0w3VumHQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.2.tgz", + "integrity": "sha512-d/gx9sRbUCVgGRGzma46wLcwiY8noTqO2Nmjg/vbTARxHRE5bdh5PIL8s97Lt1X7ScRWRW52eVYxwIoPwIdVUA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.2.tgz", + "integrity": "sha512-uqB4QsgVoro0XDC+DIftIMGXHdBPn+lK6Ek3xXAIxPMCJfqxnbtyEoMCm2KPUVOuIosd7Cyw9HawEqMFM/UA+g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/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/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.105.0.tgz", + "integrity": "sha512-7p56vRmT/z4UJ6sctGIdl5WRUQyUv53IUpUwEKi2g97zMbO7zh8ZsCLffGAvCPBR4xD72e3VXsDyLwXHhoNTMQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.105.0.tgz", + "integrity": "sha512-y+hMuDERvgg6ay1da0V1a6ZC0ZONt7dELxSFkqRZuwm7p1TTZs0MoXrPlBRflIpRoYjvzQVYxycnLImpFLhWrw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.105.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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/node": { + "version": "24.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz", + "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/pg": { + "version": "8.23.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.23.1.tgz", + "integrity": "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "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==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "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", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.2.tgz", + "integrity": "sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.152.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.12.tgz", + "integrity": "sha512-vFlTt+YEu9WBpNjdGayNmlMVzrWPi1+sv853/7t2FU7hpO5gAFBKHzQmkCq5Xjpr+evegF2994LQT3hF3mRlqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.12", + "@vitest/utils": "4.1.12", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.12.tgz", + "integrity": "sha512-fKShNpKZzupWZuY0z7egydvL0sqW4INEZBEExMBMAa6izlENEVHwoXgdaTCaBZ5fwlr4xf2o1wXC83n9RpO+NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.12", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.12.tgz", + "integrity": "sha512-XW9O8+Mxx4VpCtkYdMJLZQQwsVryP9wk7cPKDxtXFfwSak6yRh5+2fep/Y0XF/ElBvJHq8bcYdi6aeuai0opZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.12.tgz", + "integrity": "sha512-NKlVKCr5QnCe8jijdMnmkpHHU+WTqiLFR6G0pHji9uRjXK9xJl1k1nTw6F3pOOj2PDcsDt2GKQMI7wIsaT491w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.12", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.12.tgz", + "integrity": "sha512-ko1y1Qgb/slzQ9mr+b89Cuqud8WG1TKtsPD8r7ZUBdvVOGt85Af2HwCqHujrhwJq95hs/9RM7fcAPg57o1vlzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.12", + "@vitest/utils": "4.1.12", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.12.tgz", + "integrity": "sha512-RGq9x4JNGNTZHEJxvN0oSiJjeSCExm0n2gZKKJZHY9cX5AVA6M7VJILBT3ZJ/tJ/0J9uWT7fETV9/btoi//vSg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.12.tgz", + "integrity": "sha512-Q3awXrwOfEV+qc3WmO5ONyE0yO7Q1gQ9/oyHxGLZ/pl7N3NO/ZztqQwKLuwMkV+CA1JYSIeO3gkHnRDIo4AVaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.12", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv/node_modules/fast-uri": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/avvio": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.3.1.tgz", + "integrity": "sha512-QyTKW61/qlba+cUQBm/sT9X8lri4UdomqSDwCnrnj1IvLVDRRiXRkz4CgBRWB+/cmfC7SK5WQc8T1vSdGvaUcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" + } + }, + "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", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.5.tgz", + "integrity": "sha512-YMZlvWrTPQEhjKp60XSmMGt60+pffbv6DyRnOMvVO98+J7aF8w8Ui9FhdcOwvSixc/kyR6SoVvmejcn/2lZBqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.12.0", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "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==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/drizzle-orm": { + "version": "0.45.4", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.4.tgz", + "integrity": "sha512-NiM1YZpJ2UxyeE67DpDDZ+Tsw4YLX3trSlkNT7Tm9eKoUNb1LvaAaQzXgYQsWs+AQ9TUmmwMmBOCXuqqxUWKKQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@netlify/db": ">=0.4.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@netlify/db": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "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/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" + }, + "engines": { + "node": ">=18" + }, + "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/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/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/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-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stringify": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-7.2.0.tgz", + "integrity": "sha512-q18imgm9fEdQVfvJbRJALE9VP/A5HGme0+NUnWcmFYM9rbQbLkZZk20FyIp1LQ8gDOauQKwqMc52YozqFaQyOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^4.0.0", + "json-schema-ref-resolver": "^3.0.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-uri": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.2.3.tgz", + "integrity": "sha512-I1PXwP5eAhlNlcdpjt9CgB59op2ZvZfxw+BkqGFVIUOgNpCVTcTqp3HhTX7brxj5vsip+nSY4nTvff+eIA76hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastify": { + "version": "5.12.7", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.12.7.tgz", + "integrity": "sha512-v4pEPsTRPuqlDIYiQFB+BHjVN4SdHX9u0ABk0ZDUKbbdY1vsNryUd1j4Z+jQEZajvYIVk047Kimva9/AJ79GYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^4.0.5", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^7.0.0", + "find-my-way": "^9.6.0", + "light-my-request": "^6.0.0", + "pino": "^9.14.0 || ^10.1.0", + "process-warning": "^5.1.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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/find-my-way": { + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.10.0.tgz", + "integrity": "sha512-eru+uCPQ2g+nLBkf84MHP/KyNNMar0BDy0j+i8Fb72GF9/YHBZ58q7FODUtfVV7XU9nfRUHXSmaM8NmZcIbZ4Q==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz", + "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/json-schema-ref-resolver": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.1.tgz", + "integrity": "sha512-w+HwgljXpVwZ3IidoUR1qPZxSOQ+NRQWuVe6a5pe8XbHsNZE4UAwVPUvzZagn7yBKbJtt6UJnRFYbNTRPZI45Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/light-my-request": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.8.0.tgz", + "integrity": "sha512-5Q8PjoSEdtGIB8fikZsTem5vgRo4F2mR6vUKTDW/H1g6MtAt8e3duQbg/QxL5yzg6BZhor2KOUm3hhWMWLTZwg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "@fastify/deepmerge": "^3.1.0", + "cookie": "^2.0.0", + "process-warning": "^5.0.0", + "set-cookie-parser": "^3.0.1" + } + }, + "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", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.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": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.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": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.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, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.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" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lucide-react": { + "version": "0.577.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.577.0.tgz", + "integrity": "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "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/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/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": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "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/pg": { + "version": "8.23.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.1.tgz", + "integrity": "sha512-aL96AHANtWjPLDOLqnhx+ngp9+UK7ETEU8VJrDCGvsSSi/mGLcWYsS6Herg7lmaBJe4uwrfqsa7gTEFaSizDoQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "pg-connection-string": "^2.14.1", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.16.1", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.1.tgz", + "integrity": "sha512-6PQbsFWZcp9EmJEwy5cGQ2La+AMWpP46lgbb8X+U/XsHIUweYDNCpeuKck5RxL2MdVFi7krbbEi5nX4Zh7JhrQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.1.tgz", + "integrity": "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.1.tgz", + "integrity": "sha512-p9VOFMiHB/ZbJATetbg+99PxssTVSQRnyuPSQ67mN1+1KBOjZaZ83ZQzltnxPhJwSsC3nwVjJ10DVJlerbFzLg==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.4.0.tgz", + "integrity": "sha512-bk1ZMTwG/Vymx+zPoa28MhNzucFvXJWp3csrSuxuHPSMeUettaCoQ5Jvg07mtC4JGNpcfTwz41oVCnNgBK9+vA==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^1.0.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", + "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.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-warning": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.1.tgz", + "integrity": "sha512-oCkNVj4OKnKEcocVBcDfiIugN1/cE6rgfT3nCtzeqZPA8+rIxMaQ0xXzIkGP8Gj2qUaP1UR7uhJ1VD8lUCpzCg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/react-router": { + "version": "7.18.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.5.tgz", + "integrity": "sha512-dTgvcXDOrLPrlLHrcZAqWveiK1hMSqN+rGAJzegP8HtmNkfRxOoshGWBNX3ZJ0cZjOHkRdlUJtaIhZMLLyGyPw==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/react-router/node_modules/set-cookie-parser": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.3.tgz", + "integrity": "sha512-irZHtTqurLna57KQG/s+fzOwv/J6XSH4cs8M/vsrY3uCaQ3S6+QJguAH0WdN47SXlk82voMZ4naWMQf8fdEoIg==", + "license": "MIT" + }, + "node_modules/real-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz", + "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ret": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.1.tgz", + "integrity": "sha512-Z/nyvABwcRnRD2mMP7Kz6G1dFZZqX1mNErOnxF42OCQKE07b9k9hK9SQxHL3LFEZUdzQaLM4dId1E/E6Vm4WhA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.155.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-regex2": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.2.tgz", + "integrity": "sha512-tAtCGFPPP02VroDVzp4daGi6bC7jWG+41D47GKf18FeH/ubvKQHT9VmhAkvtjNOmJwqtApzWeUpEhkyEYULvmw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ret": "~0.5.0" + }, + "bin": { + "safe-regex2": "bin/safe-regex2.js" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/secure-json-parse": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.1.tgz", + "integrity": "sha512-lnd81MQEZKjhW/Gd2TQqdd4XP6QYNdDW/EP+I7nTrFHP9y2LmaGPIHk9xsnzcOiEr1c39c/e84xFgGviQKBTRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.2.1.tgz", + "integrity": "sha512-Npd1Pt+4zb3BrSZDPd2qFwRpS6b1EUEasRrO8xS0zMJi0fApXXK5YuzP0minyJnxwnFHoVoJwA55q0A0cu63gQ==", + "license": "MIT" + }, + "node_modules/shell-quote": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "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": "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/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tailwind-merge": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.7.0.tgz", + "integrity": "sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/thread-stream": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz", + "integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==", + "license": "MIT", + "dependencies": { + "real-require": "^1.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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/tinyrainbow": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/toad-cache": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.4.tgz", + "integrity": "sha512-m1TdR/rvT7kgGJZhspNtXdsdYk0fddFpJJFlG5s+UkPFo6lkLoZ3YLOaovPYjq1R75NP5JfeTlSHaOsE09peCg==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "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==", + "dev": true, + "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/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.12.tgz", + "integrity": "sha512-5eWdz2yb3ajxWF3C8C4TjkXl7FGm3n2E/VlazALrQHzK7nYMSGFnCZto0bbPNl77AwkOZVLk0YnBAejUu0NiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.12", + "@vitest/mocker": "4.1.12", + "@vitest/pretty-format": "4.1.12", + "@vitest/runner": "4.1.12", + "@vitest/snapshot": "4.1.12", + "@vitest/spy": "4.1.12", + "@vitest/utils": "4.1.12", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.12", + "@vitest/browser-preview": "4.1.12", + "@vitest/browser-webdriverio": "^4.1.11", + "@vitest/coverage-istanbul": "4.1.12", + "@vitest/coverage-v8": "4.1.12", + "@vitest/ui": "4.1.12", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/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/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.16", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.16.tgz", + "integrity": "sha512-Uv7e327ihlCfayfja3hDc0M64Upev1LWBKpRobdK4NyovDDPeeX4imWXc/wzC1yc6fkYPoAY/ncKVPAs2jrtQw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, + "packages/genetics": { + "name": "@botanica/genetics", + "version": "0.0.0" + }, + "packages/renderer": { + "name": "@botanica/renderer", + "version": "0.0.0" + }, + "packages/rules": { + "name": "@botanica/rules", + "version": "0.0.0" + }, + "packages/shared": { + "name": "@botanica/shared", + "version": "0.0.0" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..aebdfce --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "botanica", + "private": true, + "version": "0.0.0", + "workspaces": [ + "apps/*", + "packages/*" + ], + "scripts": { + "dev": "concurrently \"npm run dev -w @botanica/server\" \"npm run dev -w @botanica/web\"", + "build": "npm run build --workspaces --if-present", + "typecheck": "npm run typecheck --workspaces --if-present", + "test": "vitest run", + "db:migrate": "npm run db:migrate -w @botanica/server", + "db:seed": "npm run db:seed -w @botanica/server" + }, + "devDependencies": { + "typescript": "~5.9.3", + "vitest": "^4.1.0", + "concurrently": "^9.2.1", + "@types/node": "^24.0.0" + } +} diff --git a/packages/genetics/package.json b/packages/genetics/package.json new file mode 100644 index 0000000..e69701b --- /dev/null +++ b/packages/genetics/package.json @@ -0,0 +1,11 @@ +{ + "name": "@botanica/genetics", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": "./src/index.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + } +} diff --git a/packages/genetics/src/index.ts b/packages/genetics/src/index.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/packages/genetics/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/genetics/tsconfig.json b/packages/genetics/tsconfig.json new file mode 100644 index 0000000..01c4d9a --- /dev/null +++ b/packages/genetics/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true + }, + "include": ["src"] +} diff --git a/packages/renderer/package.json b/packages/renderer/package.json new file mode 100644 index 0000000..206dbf4 --- /dev/null +++ b/packages/renderer/package.json @@ -0,0 +1,11 @@ +{ + "name": "@botanica/renderer", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": "./src/index.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + } +} diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/packages/renderer/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/renderer/tsconfig.json b/packages/renderer/tsconfig.json new file mode 100644 index 0000000..01c4d9a --- /dev/null +++ b/packages/renderer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true + }, + "include": ["src"] +} diff --git a/packages/rules/package.json b/packages/rules/package.json new file mode 100644 index 0000000..1e22b85 --- /dev/null +++ b/packages/rules/package.json @@ -0,0 +1,11 @@ +{ + "name": "@botanica/rules", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": "./src/index.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + } +} diff --git a/packages/rules/src/index.ts b/packages/rules/src/index.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/packages/rules/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/rules/tsconfig.json b/packages/rules/tsconfig.json new file mode 100644 index 0000000..01c4d9a --- /dev/null +++ b/packages/rules/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true + }, + "include": ["src"] +} diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..b2e91ac --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,11 @@ +{ + "name": "@botanica/shared", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": "./src/index.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + } +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..01c4d9a --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true + }, + "include": ["src"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..d54705a --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +}