diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a834a89 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +.svelte-kit +build +.git +.env +.env.* +!.env.example +*.log +README.md +legacy diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f14939d --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +# Copy this file to ".env" and fill in real values. Never commit the real .env. + +# --- Database --- +# When running the app OUTSIDE Docker (npm run dev), point this at your Postgres. +# When running via docker-compose, this is set for you in docker-compose.yml. +DATABASE_URL=postgres://sde:sde_password@localhost:5432/sde_toolkit + +# --- Anthropic API (for the "AI draft" feature) --- +# Get a key from https://console.anthropic.com/ . Without it, the AI button +# fails politely and everything else keeps working. +ANTHROPIC_API_KEY= + +# Model used for AI drafting. +ANTHROPIC_MODEL=claude-sonnet-5 + +# --- Server --- +PORT=3000 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39939cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules/ +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* +*.log +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..76ad03a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# --- Build stage: install deps and compile the SvelteKit app --- +FROM node:22-alpine AS build +WORKDIR /app + +# Install dependencies first (better layer caching) +COPY package.json package-lock.json* ./ +RUN npm ci + +# Build the app +COPY . . +RUN npm run build + +# Drop dev dependencies so the runtime image stays small +RUN npm prune --omit=dev + +# --- Runtime stage: just Node + the built server --- +FROM node:22-alpine AS runtime +WORKDIR /app +ENV NODE_ENV=production + +COPY --from=build /app/build ./build +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/package.json ./package.json + +EXPOSE 3000 +CMD ["node", "build"] diff --git a/README.md b/README.md index 69640d7..a1c7de3 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,106 @@ # Project SDE Meeting Toolkit -## Contents +A shared, multi-user web app with two meeting tools: -| File | Purpose | +- **Field Problem Workshop** — capture field problems, cluster them into root problems, dot-vote, build "breadcrumb" trails to measurable outcomes, and assign actions. Includes an optional AI draft feature. +- **Scope Lock Meeting Suite** — Scope Boundary Board, MVP Priority Ranker, Decision Registry, and Micron Pilot Readiness. + +Unlike the original single-file HTML versions (kept in [`legacy/`](./legacy) for reference), everyone who opens the same link works from the **same data**, stored on a shared server. Edits autosave; a version check prevents two people from silently overwriting each other. + +## Tech stack + +| Piece | Choice | Why | +|---|---|---| +| Framework | SvelteKit (Svelte 5) + TypeScript | Frontend and server API in one project | +| Server | Node (`adapter-node`) | Runs as a plain server, easy to containerize | +| Database | PostgreSQL | Shared storage for all users | +| Packaging | Docker + docker-compose | One command to run the whole thing internally | + +## Project layout + +``` +src/ + app.css shared design tokens + base styles + lib/ + types.ts data shapes for both tools + workspace.svelte.ts client sync: load / autosave / poll / conflict handling + toast.svelte.ts shared toast controller + components/ Toast, SyncBadge + defaults/ seeded baseline state for each tool + server/ + db/ Postgres pool + workspace repository + ai.ts Anthropic proxy (holds the API key server-side) + routes/ + +page.svelte home / tool picker + workshop/ Field Problem Workshop + scope-lock/ Scope Lock Meeting Suite + api/ + workspaces/[tool]/[name]/ GET (load) + PUT (save) + workspaces/[tool]/[name]/reset/ POST (reset to baseline) + ai/draft/ POST (AI breadcrumb draft) +db/init.sql database schema (runs once on first DB startup) +Dockerfile, docker-compose.yml +legacy/ the original single-file HTML tools +``` + +## Run it with Docker (recommended) + +Requires Docker Desktop / Docker Engine. + +```bash +# optional: enable the AI feature by putting a key in your shell/.env first +# ANTHROPIC_API_KEY=sk-ant-... + +docker compose up -d --build +``` + +Then open **http://localhost:3000**. To share on your network, others use `http://:3000`. + +```bash +docker compose down # stop +docker compose down -v # stop AND erase all saved data (reset the database) +docker compose logs -f app # watch app logs +``` + +The database persists in a Docker volume (`db_data`), so your data survives restarts. + +## Run it for development (without Docker) + +You need Node 20+ and a PostgreSQL you can connect to. + +```bash +cp .env.example .env # then edit DATABASE_URL (and ANTHROPIC_API_KEY if wanted) +npm install +# create the schema in your database: +psql "$DATABASE_URL" -f db/init.sql +npm run dev # http://localhost:5173 +``` + +Other scripts: `npm run build` (production build), `npm run preview` (serve the build), `npm run check` (type-check). + +## Configuration + +All config is via environment variables (see `.env.example`): + +| Variable | Purpose | |---|---| -| SDE_Field_Problem_Workshop_v5.html | Field problem workshop: Problem Map (capture, clusters, dot voting) → Breadcrumbs (with AI draft) → Actions. Pre-loaded with the July 21 session baseline. | -| SDE_Scope_Lock_Meeting_Suite.html | Scope lock meeting suite: Scope Boundary Board, MVP Priority Ranker, Decision Registry, Micron Pilot Readiness. | -| construction-breadcrumbs.skill | Claude skill encoding the breadcrumb methodology (EVM, CII AWP, Last Planner, first-time quality). Install via Save skill in Claude to use it in any chat. | +| `DATABASE_URL` | Postgres connection string. Set automatically by docker-compose. | +| `ANTHROPIC_API_KEY` | Enables the "AI draft" button. Leave blank to disable it (the button then fails politely). | +| `ANTHROPIC_MODEL` | Model for AI drafting (default `claude-sonnet-5`). | +| `PORT` | Server port (default 3000). | +| `ORIGIN` | Public URL of the app, e.g. `http://server:3000`. Needed by the Node server for form/security handling. | -## Running the workshop tool +## How the shared-workspace model works -- **With AI features:** open the HTML file as an artifact inside Claude (upload it to a chat and ask Claude to render it, or keep it in a Project). The AI draft fields button on Breadcrumbs requires the Claude environment and network connectivity. -- **Without AI:** open the file directly in any browser. Everything works except the AI button, which fails politely. +- Each tool has a named workspace (default: `default`) stored as one JSON document with a `version` number. +- When you edit, the browser autosaves after a short pause, sending your changes **plus the version you started from**. +- If a teammate saved first, the server rejects your save (HTTP 409) and hands back their current copy, which your page loads — so nobody's work is silently overwritten. The status badge in the header shows this. +- Open pages poll every few seconds, so a teammate's saved changes show up without a manual refresh. -## Persistence +This fits turn-taking meeting use. It is **not** live character-by-character co-editing (like Google Docs). If that's needed later, the version/polling foundation here can be upgraded to websockets. -- The workshop tool holds state in the session only. **Save JSON before closing** and Load JSON to resume. The exported JSON is the working record; the Export summary markdown is the meeting artifact. -- The Scope Lock suite autosaves to the browser (localStorage) on the machine where it is opened; its Export button produces the durable record. +## Notes / possible next steps -## Reset behavior - -The workshop tool's Reset returns to the clustered July 21 baseline (43 problems in 7 root clusters, 6 breadcrumbs, 5 actions). The Scope Lock suite's Reset returns to its pre-seeded starting state. +- **Authentication**: there is none yet — anyone with the link can view and edit. The data model already has an `updated_by` column ready for it. +- **Multiple named workspaces**: the API supports any name (`/api/workspaces/workshop/`); the UI currently uses `default`. Adding a workspace picker is straightforward. +- **The `.skill` file** in `legacy/` is a separate Claude skill, unrelated to running the website. diff --git a/db/init.sql b/db/init.sql new file mode 100644 index 0000000..c8602b6 --- /dev/null +++ b/db/init.sql @@ -0,0 +1,16 @@ +-- Schema for the SDE meeting toolkit. +-- +-- Design note: each tool (workshop, scope-lock) stores its data as a single +-- named "workspace" whose state is a JSON document. A version counter gives us +-- optimistic concurrency: a save only succeeds if nobody else saved since you +-- loaded, so two people in the same workspace can't silently clobber each other. + +CREATE TABLE IF NOT EXISTS workspaces ( + tool TEXT NOT NULL, -- 'workshop' | 'scope-lock' + name TEXT NOT NULL, -- workspace name, e.g. 'default' + state JSONB NOT NULL, -- the whole tool state + version INTEGER NOT NULL DEFAULT 1, + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_by TEXT, -- optional display name of last editor + PRIMARY KEY (tool, name) +); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cb4759c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +# One command to run the whole thing internally: docker compose up -d +# The app comes up on http://:3000 + +services: + db: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_USER: sde + POSTGRES_PASSWORD: sde_password + POSTGRES_DB: sde_toolkit + volumes: + - db_data:/var/lib/postgresql/data + # Runs once on first startup to create the schema. + - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U sde -d sde_toolkit"] + interval: 5s + timeout: 5s + retries: 10 + + app: + build: . + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + DATABASE_URL: postgres://sde:sde_password@db:5432/sde_toolkit + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} + ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-5} + PORT: 3000 + ORIGIN: ${ORIGIN:-http://localhost:3000} + ports: + - "3000:3000" + +volumes: + db_data: diff --git a/SDE_Field_Problem_Workshop_v5.html b/legacy/SDE_Field_Problem_Workshop_v5.html similarity index 100% rename from SDE_Field_Problem_Workshop_v5.html rename to legacy/SDE_Field_Problem_Workshop_v5.html diff --git a/SDE_Scope_Lock_Meeting_Suite.html b/legacy/SDE_Scope_Lock_Meeting_Suite.html similarity index 100% rename from SDE_Scope_Lock_Meeting_Suite.html rename to legacy/SDE_Scope_Lock_Meeting_Suite.html diff --git a/construction-breadcrumbs.skill b/legacy/construction-breadcrumbs.skill similarity index 100% rename from construction-breadcrumbs.skill rename to legacy/construction-breadcrumbs.skill diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8a1e33e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2396 @@ +{ + "name": "sde-meeting-toolkit", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sde-meeting-toolkit", + "version": "1.0.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.32.0", + "pg": "^8.13.0" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.2.9", + "@sveltejs/kit": "^2.8.0", + "@sveltejs/vite-plugin-svelte": "^4.0.0", + "@types/pg": "^8.20.0", + "svelte": "^5.1.0", + "svelte-check": "^4.0.0", + "typescript": "^5.6.0", + "vite": "^5.4.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.32.1.tgz", + "integrity": "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==", + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", + "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", + "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/@sveltejs/acorn-typescript": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.11.tgz", + "integrity": "sha512-LFuZUkjJ9iF7JZye/aG5XM0SFcQ5VyL0oVX4WJ9dc0Va3R3s0OauX1BESVCb+YN/ol8TAfqGDDAQsTG627Y5kw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.5.7.tgz", + "integrity": "sha512-uOfc9eVlI3A37RRSaKcgrheBYPrfJwC9VMqDp8x/O6tlKdcLLvHThSWD0KNIbjQ/d+7bwLGx3vx6aowAcRfd2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-replace": "^6.0.3", + "rollup": "^4.59.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.70.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.70.1.tgz", + "integrity": "sha512-nY9SPHGOZro3doud9vZXDBwl9tCZIouuJztjgSHs6PAIrv9M/z5O7eOhPV5xU7CgVHA976Jwu3BA1hIFvXztkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.9", + "@types/cookie": "^0.6.0", + "acorn": "^8.16.0", + "cookie": "^0.6.0", + "devalue": "^5.8.1", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/load-config": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz", + "integrity": "sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.4.tgz", + "integrity": "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", + "debug": "^4.3.7", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.12", + "vitefu": "^1.0.3" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0-next.96 || ^5.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz", + "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.7" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", + "svelte": "^5.0.0-next.96 || ^5.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "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": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@types/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/devalue": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.2.tgz", + "integrity": "sha512-DObPPAfdtFbXjxLqK8s2Xk9ZuWz5+ZoFEhC7J76es4GU/rEiXwHTmbImoCdyoCOcBH1UF3+Cz6Z2sYD4hyl5TA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.3.0.tgz", + "integrity": "sha512-GQ/7RN8uOtEfNpzZzBMTzW9JBcX42oaSVtPzdF+6cEL8pqIL094iUpr9jzYGn4O4P/1S60dJ6izyT8F4LYARng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "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/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz", + "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.15.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", + "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.15.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", + "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", + "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.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz", + "integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==", + "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.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "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/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz", + "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.56.7", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.7.tgz", + "integrity": "sha512-5qERUZX80oQj6XrDMUmD2Uhd/cIpCPDWWKBK3ZHmyRUC9apPyamWM8xMo31mbWsIQxwG2hVoSnOJ/EcnhVkkzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.10", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.8.1", + "esm-env": "^1.2.1", + "esrap": "^2.2.12", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.3.tgz", + "integrity": "sha512-DHdTCGX62R0fCxBEaT+USdASAnoaRBaaNczkRJl0K7o3WyoCeVUbVxo6fKqpOll/B+WMWCsiFK0eFrJSNBKZIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "@sveltejs/load-config": "^0.2.0", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte/node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "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": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "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/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0f1f0b3 --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "sde-meeting-toolkit", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "Project SDE meeting toolkit: Field Problem Workshop and Scope Lock Suite as a shared multi-user web app.", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "start": "node build", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "db:init": "node scripts/db-init.js" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.2.9", + "@sveltejs/kit": "^2.8.0", + "@sveltejs/vite-plugin-svelte": "^4.0.0", + "@types/pg": "^8.20.0", + "svelte": "^5.1.0", + "svelte-check": "^4.0.0", + "typescript": "^5.6.0", + "vite": "^5.4.0" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.32.0", + "pg": "^8.13.0" + } +} diff --git a/src/app.css b/src/app.css new file mode 100644 index 0000000..4dac305 --- /dev/null +++ b/src/app.css @@ -0,0 +1,59 @@ +/* Shared design tokens and base styles, carried over from the original tools + so the look and feel stays consistent across both apps. */ +:root { + --bg: #f4f4f4; + --layer: #ffffff; + --layer-hover: #e8e8e8; + --border: #e0e0e0; + --border-strong: #8d8d8d; + --text: #161616; + --text-secondary: #525252; + --text-helper: #6f6f6f; + --interactive: #0f62fe; + --interactive-hover: #0353e9; + --danger: #da1e28; + --success: #24a148; + --warning-bg: #fdf6dd; + --warning: #8e6a00; + --purple: #8a3ffc; + --teal: #007d79; + --gray-tag: #e0e0e0; + --focus: #0f62fe; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, -apple-system, sans-serif; + background: var(--bg); + color: var(--text); + font-size: 16px; + line-height: 1.4; +} + +button { + font-family: inherit; + cursor: pointer; +} + +button:focus-visible, +input:focus-visible, +select:focus-visible, +textarea:focus-visible { + outline: 2px solid var(--focus); + outline-offset: 1px; +} + +a { + color: var(--interactive); +} + +@media (prefers-reduced-motion: reduce) { + * { + transition: none !important; + } +} diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..0d053c2 --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,12 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..97f47a1 --- /dev/null +++ b/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/components/SyncBadge.svelte b/src/lib/components/SyncBadge.svelte new file mode 100644 index 0000000..46a5203 --- /dev/null +++ b/src/lib/components/SyncBadge.svelte @@ -0,0 +1,49 @@ + + + + {labels[status]} + + + diff --git a/src/lib/components/Toast.svelte b/src/lib/components/Toast.svelte new file mode 100644 index 0000000..3607f32 --- /dev/null +++ b/src/lib/components/Toast.svelte @@ -0,0 +1,25 @@ + + +
{toast.message}
+ + diff --git a/src/lib/defaults/index.ts b/src/lib/defaults/index.ts new file mode 100644 index 0000000..736821c --- /dev/null +++ b/src/lib/defaults/index.ts @@ -0,0 +1,33 @@ +import type { ToolId } from '$lib/types'; +import { workshopDefaultState } from './workshop'; +import { scopeLockDefaultState } from './scope-lock'; + +/** The tools known to the app, with display metadata for the home page. */ +export const TOOLS: { id: ToolId; title: string; blurb: string }[] = [ + { + id: 'workshop', + title: 'Field Problem Workshop', + blurb: 'Capture field problems, cluster them into root problems, vote, build breadcrumb trails to measurable outcomes, and assign actions.' + }, + { + id: 'scope-lock', + title: 'Scope Lock Meeting Suite', + blurb: 'Scope Boundary Board, MVP Priority Ranker, Decision Registry, and Micron Pilot Readiness in one place.' + } +]; + +/** Return a fresh copy of the baseline state for a tool. */ +export function defaultStateFor(tool: ToolId): unknown { + switch (tool) { + case 'workshop': + return workshopDefaultState(); + case 'scope-lock': + return scopeLockDefaultState(); + default: + throw new Error(`Unknown tool: ${tool satisfies never}`); + } +} + +export function isToolId(value: string): value is ToolId { + return value === 'workshop' || value === 'scope-lock'; +} diff --git a/src/lib/defaults/scope-lock.ts b/src/lib/defaults/scope-lock.ts new file mode 100644 index 0000000..473bf7a --- /dev/null +++ b/src/lib/defaults/scope-lock.ts @@ -0,0 +1,90 @@ +// Baseline state for the Scope Lock Meeting Suite (Micron pilot starting point). + +import type { ScopeLockState } from '$lib/types'; + +export const DECISION_TYPES = ['Scope', 'Technical', 'Process', 'Data', 'Pilot', 'Other']; +export const DECISION_OWNERS = [ + 'Nick', + 'Tim', + 'Cameron', + 'Clinton', + 'Jason', + 'Wyman', + 'Ryan', + 'Brendan', + 'Lee', + 'Team', + 'Other' +]; +export const DECISION_STATUSES = ['Locked', 'Provisional', 'Revisit'] as const; + +// Readiness status labels, indexed by ScopeLock ready item `s`. +export const READY_STATES = ['Not started', 'In progress', 'Ready', 'Blocked']; + +export function scopeLockDefaultState(): ScopeLockState { + return { + scope: [ + // Locked pre-decisions surfaced so the room sees them, not to relitigate them + { id: 's1', t: 'IO list (types + instances, COIN schema)', b: 'mvp', lock: true }, + { id: 's2', t: 'Cable list', b: 'mvp', lock: true }, + { id: 's3', t: 'Wire list', b: 'mvp', lock: true }, + { id: 's4', t: 'Construction status tracking (asset level)', b: 'mvp', lock: true }, + { id: 's5', t: 'Commissioning / Cx workflows and signoffs', b: 'out', lock: true }, + { id: 's6', t: 'Blender / 3D visualization app', b: 'later', lock: true }, + // Open items, unsorted + { id: 's7', t: 'Mobile field entry (phone / tablet)', b: '', lock: false }, + { id: 's8', t: 'Offline mode with sync', b: '', lock: false }, + { id: 's9', t: 'Photo attachments on status updates', b: '', lock: false }, + { id: 's10', t: 'Progress rollups by system / area', b: '', lock: false }, + { id: 's11', t: 'Weighted progress rules (per asset class / stage)', b: '', lock: false }, + { id: 's12', t: 'Plan vs actual variance reporting', b: '', lock: false }, + { id: 's13', t: 'Standup / weekly progress report export', b: '', lock: false }, + { id: 's14', t: 'Role based access (PM / Field / QC / Admin)', b: '', lock: false }, + { id: 's15', t: 'QC hold points inside construction tracking', b: '', lock: false }, + { id: 's16', t: 'IWP / work package linkage (AWP alignment)', b: '', lock: false }, + { id: 's17', t: 'controls.dev API read/write for field updates', b: '', lock: false }, + { id: 's18', t: 'PrimeFlow import (legacy IO data)', b: '', lock: false }, + { id: 's19', t: 'Acumatica integration (cost codes / hours)', b: '', lock: false }, + { id: 's20', t: 'Parts / device library management', b: '', lock: false }, + { id: 's21', t: 'Panel / termination level granularity', b: '', lock: false }, + { id: 's22', t: 'Dashboards with R/Y/G indicators', b: '', lock: false }, + { id: 's23', t: 'Labor / crew hours tracking', b: '', lock: false }, + { id: 's24', t: 'GC / owner tool data exchange (Micron side)', b: '', lock: false }, + { id: 's25', t: 'Project templates and cloning', b: '', lock: false }, + { id: 's26', t: 'Audit trail (user / timestamp on every update)', b: '', lock: false }, + { id: 's27', t: 'Change / revision handling for engineering data', b: '', lock: false }, + { id: 's28', t: 'Excel import/export round trip', b: '', lock: false }, + { id: 's29', t: 'Training materials and quick start guides', b: '', lock: false }, + { id: 's30', t: 'In app notifications / @mentions', b: '', lock: false } + ], + rank: [ + { id: 'r1', t: 'Mobile field entry', n: 'Named top priority in prior sessions' }, + { id: 'r2', t: 'Reporting (standup + rollups)', n: '' }, + { id: 'r3', t: 'Integration and phasing (controls.dev sync)', n: '' }, + { id: 'r4', t: 'Role based access', n: '' }, + { id: 'r5', t: 'Ease of training and adoption', n: '' }, + { id: 'r6', t: 'Plan vs actual variance', n: '' }, + { id: 'r7', t: 'Weighted progress rules', n: '' } + ], + cutAfter: 5, + decisions: [], + ready: [ + { id: 'd1', g: 'Data', t: 'Micron IO list source identified and format confirmed', w: 'Which system of record, which export format, who provides it', o: '', s: 0 }, + { id: 'd2', g: 'Data', t: 'Cable and wire list data availability confirmed', w: '', o: '', s: 0 }, + { id: 'd3', g: 'Data', t: 'Asset hierarchy agreed (Building > System > Panel > Device > Termination)', w: 'Depth of tracking granularity for the pilot', o: '', s: 0 }, + { id: 'd4', g: 'Data', t: 'Import format and refresh cadence agreed with engineering', w: 'One time load vs live sync for the pilot', o: '', s: 0 }, + { id: 'd5', g: 'Platform', t: 'controls.dev instance provisioned for Micron project', w: '', o: 'Cameron', s: 0 }, + { id: 'd6', g: 'Platform', t: 'API endpoints ready for field status writes', w: '', o: 'Cameron', s: 0 }, + { id: 'd7', g: 'Platform', t: 'Role definitions configured (PM / Field / QC / Admin)', w: '', o: '', s: 0 }, + { id: 'd8', g: 'Field', t: 'Field devices confirmed (tablets / phones, count and type)', w: '', o: 'Wyman', s: 0 }, + { id: 'd9', g: 'Field', t: 'Fab site network / device security constraints understood', w: 'Semiconductor site restrictions on devices, cameras, connectivity', o: 'Wyman', s: 0 }, + { id: 'd10', g: 'Field', t: 'Field team availability windows confirmed with Micron schedule', w: '', o: 'Wyman', s: 0 }, + { id: 'd11', g: 'People', t: 'Pilot cohort named, including at least one named skeptic', w: 'Change management framework requirement', o: 'Nick', s: 0 }, + { id: 'd12', g: 'People', t: 'Friction routing loop defined (who owns fixing what the field reports)', w: '', o: '', s: 0 }, + { id: 'd13', g: 'People', t: 'Training approach and materials scoped', w: '', o: '', s: 0 }, + { id: 'd14', g: 'Governance', t: 'Pilot success metrics defined (behavioral, not sentiment)', w: 'Update compliance, entry latency, duplicate entry eliminated; not satisfaction scores', o: 'Nick', s: 0 }, + { id: 'd15', g: 'Governance', t: 'Explicit stop path defined (what triggers pausing the pilot)', w: '', o: '', s: 0 }, + { id: 'd16', g: 'Governance', t: 'Baseline current state process documented before go live', w: 'Avoids reconciliation gap with change management framework', o: '', s: 0 } + ] + }; +} diff --git a/src/lib/defaults/workshop.ts b/src/lib/defaults/workshop.ts new file mode 100644 index 0000000..50c86f8 --- /dev/null +++ b/src/lib/defaults/workshop.ts @@ -0,0 +1,189 @@ +// Baseline state and static reference data for the Field Problem Workshop. +// This is the "July 21 session" starting point the tool resets to. + +import type { + Breadcrumb, + Cluster, + Problem, + ProblemType, + WorkshopState +} from '$lib/types'; + +export const PEOPLE = [ + 'Nick', + 'Tim', + 'Jason', + 'Brendan', + 'Lee', + 'Cody', + 'Clinton', + 'Wyman', + 'Cameron', + 'Ryan', + 'Team' +]; + +export const TYPES: ProblemType[] = ['Problem', 'Question', 'Idea']; + +// Theme-analysis suggestions shown as "ghost" rows inside each cluster. +export const SUGGESTIONS: { cluster: string; t: string }[] = [ + { cluster: 'c1', t: 'There is no shared definition of what counts as complete for each asset stage, so reported percent complete means different things by team' }, + { cluster: 'c1', t: 'Percent complete is count based, not effort based: a 2 hour device and a 2 day panel move the number the same amount' }, + { cluster: 'c1', t: 'Turnover readiness by system is not visible, so incomplete predecessor work is discovered at commissioning' }, + { cluster: 'c2', t: 'The field gets no feedback that their updates were used, so the discipline to enter data decays' }, + { cluster: 'c2', t: 'As-built and redline data is not captured at completion, so closeout becomes a reconstruction project' }, + { cluster: 'c2', t: 'Each project invents its own tracker (Excel, paper, whiteboard), so nothing rolls up across projects' }, + { cluster: 'c3', t: 'Permits, LOTO, and energization requirements are not confirmed before work is released, causing same day stand downs' }, + { cluster: 'c3', t: 'Work areas are not confirmed released by the GC or other trades before crews are sent' }, + { cluster: 'c3', t: 'There is no defined readiness check before work is released; each foreman decides on their own what to verify' }, + { cluster: 'c4', t: 'Crews do not see a lookahead: the plan lives with the PM or GF, so the next assignment travels verbally' }, + { cluster: 'c4', t: 'Commitments made at standup are not tracked, so we never know what percent of promised work actually completed' }, + { cluster: 'c4', t: 'There is no visibility to which crews or skills free up when, so reassignment is reactive' }, + { cluster: 'c5', t: 'Extra work and T&M are not captured at the time they are performed, so recoverable cost is lost' }, + { cluster: 'c5', t: 'There is no single owner of the change log; pending changes live in email threads' }, + { cluster: 'c5', t: 'Scope splits at contract boundaries are not documented anywhere the field can check' }, + { cluster: 'c6', t: 'There is no traceable record of who installed or verified a termination, so failure investigations start from zero' }, + { cluster: 'c6', t: 'The field cannot tell whether the drawing in hand is the current revision without calling the office' }, + { cluster: 'c6', t: 'QA hold points are not enforced by the process; skipping a gate has no visible consequence until rework' }, + { cluster: 'c7', t: 'Estimating does not receive field actuals in a usable form, so estimates cannot learn from performance' } +]; + +function cluster(id: string, name: string, root: string, ws = ''): Cluster { + return { id, name, root, ws, open: false, dots: 0, promoted: false }; +} + +function problem( + id: string, + t: string, + who: string, + cluster: string, + type: ProblemType = 'Problem' +): Problem { + return { id, t, who, cluster, type, sugg: false }; +} + +function breadcrumb(o: Partial & { id: string }): Breadcrumb { + return { + example: false, + type: 'Outcome', + prob: '', + related: [], + driver: '', + cap: '', + metric: '', + method: '', + baseline: '', + target: '', + ...o + }; +} + +export function workshopDefaultState(): WorkshopState { + return { + clusters: [ + cluster('c1', 'Progress Visibility', 'We cannot answer where a project stands: no trustworthy, phase level percent complete exists', 'Tracking MVP (pilot)'), + cluster('c2', 'Data Freshness & Entry Burden', 'Field data is captured late, on paper, or more than once, so the record is stale and expensive to maintain'), + cluster('c3', 'Constraint Readiness', 'Work is released before it is ready: material, tools, and information are not confirmed available before crews start', 'IWP Tool Suite'), + cluster('c4', 'Planning & Resources', 'Crews and planners lack visibility to backlog, priorities, and resource availability, producing idle time and churn'), + cluster('c5', 'Scope Control', 'Scope changes and gaps move faster than our change control: work happens without approval or falls out entirely', 'AWP Process RFP'), + cluster('c6', 'Quality & Spec Conformance', 'The field cannot reliably build to the approved design: specs, revisions, and QA gates do not reach the point of work', 'AWP Process RFP'), + cluster('c7', 'Knowledge & Integration', 'What we learn does not travel: knowledge stays with individuals and external platforms stay closed to us') + ], + problems: [ + problem('p01', 'No one can reliably answer "where are we at / what percent complete" on a project', 'Jason', 'c1'), + problem('p05', 'Percent complete is needed by phase (engineering, BIM, tray/conduit), not just one overall number', 'Jason', 'c1'), + problem('p27', 'Work started but not finished. (ie field hits constraint and forgets to return to the scope)', 'Cody', 'c1'), + problem('p17', 'Constraints (ie trade stacking) not being captured in real time. No visibility to GF / Super loosing the ability to better communicate at stand up meetings', 'Nick', 'c1'), + problem('p34', 'Install at risk - BIM has no visibility to what has been installed without being captured in the model.', 'Team', 'c1'), + problem('p02', 'Field data goes stale because updates are not entered when the work completes', 'Tim', 'c2'), + problem('p37', 'Data is being captured in the field on paper and brought back to the office to be converted to digital (ie QA check lists, Time sheets, daily log)', 'Team', 'c2'), + problem('p08', 'Crews walk back to the trailer to turn in daily logs and forms', 'Tim', 'c2'), + problem('p09', 'Getting assets, IO, and cable lists into a workable relational database takes heavy manual entry', 'Jason', 'c2'), + problem('p10', 'Double entry between our tools and the GC platform on every project', 'Jason', 'c2'), + problem('p36', 'cable pull list has duplicate or missing cable', 'Team', 'c2'), + problem('p28', 'material is not available when the scope is executed.', 'Nick', 'c3'), + problem('p29', 'Equipment / tools are not available when the scope is being executed (ladders, lift)', 'Team', 'c3'), + problem('p06', 'We overspend on material because nobody knows where material is', 'Nick', 'c3'), + problem('p18', 'working with incomplete data sets (ie installing with no BIM)', 'Cody', 'c3'), + problem('p22', 'poor visibility to predecessors / requirements for the work being done. (ie if a peice of equipment needs to be commissioned but it requires PTP backbone to be operational on the other side of the building))', 'Tim', 'c3'), + problem('p04', 'Idle time: people stand around waiting for their next assignment', 'Tim', 'c4'), + problem('p14', 'Foreman dont have good visibility to work backlog causing Idle time.', 'Nick', 'c4'), + problem('p16', 'Bim starts work on priority 1, then told to stop and work on priority 3, this repeats', 'Cody', 'c4'), + problem('p19', 'Resource availability is not availible to project teams (ie if a PM needs a tech, where can they get one?)', 'Nick', 'c4'), + problem('p42', 'Progress halts due to loss of resource (ie pto, terminated)', 'Clinton', 'c4'), + problem('p30', 'Scope takes longer than expected (could this be mitigated by including the field team in estimates)', 'Team', 'c4'), + problem('p03', 'scope gap - scope performed without approved change order.', 'Clinton', 'c5'), + problem('p40', 'scope gap - in scope not performed as it was lost in pre-contruction activities (ie dropped from the io list)', 'Clinton', 'c5'), + problem('p41', 'Scope Gap - who terminates the back bone fiber?', 'Clinton', 'c5'), + problem('p35', 'Field team taking direction from external sources causing rework as it was not approved through the proper channels.', 'Team', 'c5'), + problem('p33', 'When field team deviates from plan failure to communicate that back to design (ie redlines during the course of work rather than at the end)', 'Team', 'c5'), + problem('p07', 'Field rework happens because crews do not have the latest drawings', 'Nick', 'c6'), + problem('p15', 'Rework caused by construction tracking not having the correct data', 'Cody', 'c6'), + problem('p23', 'Failure in sequencing, missing the QA gate (ie terminating a cable that is TSP when it should have been a 4 Conductor)', 'Cody', 'c6'), + problem('p24', 'wrong type of conduit installed due to environmental requirements ( ie Class 1 div 2 area had EMT installed) failure mode: out of spec', 'Clinton', 'c6'), + problem('p20', 'installed equipment doesnt match submittals', 'Brendan', 'c6'), + problem('p21', 'install material used was does not match whats approved in the submittal', 'Nick', 'c6'), + problem('p25', 'Specs not accessible to field installation teams', 'Clinton', 'c6'), + problem('p31', 'visability to chain of custody of project scope, drawings, design intent (who do I ask when constructability issue arises)', 'Team', 'c6'), + problem('p32', 'field teams fail to follow the designed workflow/ install.', 'Team', 'c6'), + problem('p13', 'Punch list / issue follow-through is not measured; time to close is unknown', 'Nick', 'c7'), + problem('p43', 'Tribal knowledge is lost after someone leaves or is on PTO', 'Clinton', 'c7'), + problem('p38', 'Lessons learned - Construction methods could be more efficient with one team and this is not shared with the other.', 'Team', 'c7'), + problem('p39', 'What are all of the ways we are reinventing the wheel?', 'Team', 'c7', 'Question'), + problem('p26', 'Suggestion improvement: create info graphics and other simplified ways of communicating project specs', 'Clinton', 'c7', 'Idea'), + problem('p11', 'GC platform sync/access requests get denied; we have never reached the people who actually own the decision', 'Tim', 'c7'), + problem('p12', 'Platform changes GC to GC, so crews face retraining on a new tool nearly every project', 'Brendan', 'c7') + ], + breadcrumbs: [ + breadcrumb({ + id: 'b1', + example: true, + type: 'Outcome', + prob: 'Crews walk back to the trailer to turn in daily logs and forms', + driver: 'Minimize non productive field time; keep craft hours on the work face', + cap: 'Mobile field entry from the work area (phone / tablet, offline capable)', + metric: '% of daily logs submitted from the work area on the same day', + method: 'System timestamps on log submission vs work date', + baseline: 'Field poll of minutes per day spent on paperwork and walking, taken before pilot' + }), + breadcrumb({ + id: 'b2', + type: 'Outcome', + prob: 'No one can reliably answer "where are we at / what percent complete" on a project', + driver: 'man power / work planning', + cap: 'construction tracking / resource planning', + metric: 'do we have a tool, tool has been implemented and adopted, ~>80% visibility to the project progress' + }), + breadcrumb({ id: 'b3', type: 'Outcome', prob: 'Field data goes stale because updates are not entered when the work completes' }), + breadcrumb({ id: 'b4', type: 'Outcome', prob: 'scope gap - scope performed without approved change order.' }), + breadcrumb({ + id: 'b5', + type: 'Outcome', + prob: 'Double entry between our tools and the GC platform on every project', + driver: 'Reduced duplicate entry', + cap: 'Single system of entry with automated propagation', + metric: 'Number of systems requiring manual entry of the same record', + method: 'Trace 10 representative records end to end', + baseline: 'Count on a current project before pilot', + target: '1 internal system of entry' + }), + breadcrumb({ + id: 'b6', + type: 'Adoption', + prob: 'Supporting adoption measure for the pilot', + driver: 'Field usage', + cap: 'Pilot tool in daily field use', + metric: '% of assigned field users active weekly', + method: 'Platform usage analytics', + baseline: 'n/a, starts at zero' + }) + ], + actions: [ + { id: 'a1', t: 'Review cluster assignments and root problem statements as a team; move issues that landed in the wrong cluster', owner: '', due: '', done: false }, + { id: 'a2', t: 'Complete breadcrumb: Field data goes stale because updates are not entered when the work completes', owner: '', due: '', done: false }, + { id: 'a3', t: 'Complete breadcrumb: scope gap - scope performed without approved change order.', owner: '', due: '', done: false }, + { id: 'a4', t: 'Rework the percent complete breadcrumb: current metric is an adoption statement, needs a real outcome metric with method and baseline', owner: '', due: '', done: false }, + { id: 'a5', t: 'Assign workstreams to the three unmapped clusters (Data Freshness, Planning & Resources, Knowledge & Integration)', owner: '', due: '', done: false } + ] + }; +} diff --git a/src/lib/server/ai.ts b/src/lib/server/ai.ts new file mode 100644 index 0000000..ff05e71 --- /dev/null +++ b/src/lib/server/ai.ts @@ -0,0 +1,83 @@ +// Server-side "AI draft" for breadcrumb fields. The Anthropic API key lives here +// (from an env var) and never reaches the browser. + +import Anthropic from '@anthropic-ai/sdk'; +import { env } from '$env/dynamic/private'; + +export const REQUIRED_FIELDS = ['driver', 'cap', 'metric', 'method', 'baseline'] as const; +export const ADOPTION_WORDS = + /implement|adopt|roll ?out|have a tool|tool exists|usage|active users?|users? active/i; + +export interface DraftResult { + domain: string; + driver: string; + cap: string; + metric: string; + method: string; + baseline: string; +} + +const METHOD_KB = `Domain playbook (anchor methodology -> standard metrics/methods/baselines): +1 PROGRESS VISIBILITY - Earned Value Mgmt, rules of credit. Metrics: claiming accuracy (reported % vs field-verified % within tolerance, by phase; method: monthly spot audit of 30+ sampled assets; baseline: first audit); forecast reliability (forecast vs actual completion; baseline: last project's reports). Never use "% of assets with a status" (adoption in disguise). +2 DATA FRESHNESS / ENTRY BURDEN - lean data flow, single source of truth. Metrics: entry latency (% of updates within 24h of completion; method: system timestamp vs completion date, weekly; baseline: audit paper-to-digital lag on current project); touch count (systems the same record is entered into; method: trace 10 records end to end; baseline: pre-pilot trace); non-productive data minutes/person/day (method+baseline: structured field poll pre-pilot, repeat midpoint). +3 CONSTRAINT READINESS / WORK RELEASE - CII AWP IR 272-2, constraint-free IWP release (drawings, material, tools, labor, access, predecessors, permits, quality docs, scaffolding). Metrics: % releases issued constraint-free (method: release-gate checklist audit weekly, later constraint stop = miss; baseline: audit last month of work starts from daily logs); stand-down count/week (method: foreman log with fixed reason list; baseline: 2-week logging exercise); tool time % (method: activity sampling study; baseline: pre-pilot study; propose only when stakes justify cost). +4 PLANNING / RESOURCES / IDLE - Last Planner System. Metrics: PPC (% weekly commitments completed as promised; method: log at weekly planning, score next week, publish trend; baseline: first 4 weeks of logging); reasons-for-variance distribution (fixed-list coding of misses); idle hours/crew-week (timecard or foreman reason codes; baseline: 2-week coding). +5 SCOPE CONTROL / CHANGE - PMBOK change control, CII change research. Metrics: unauthorized-work hours/dollars at time of performance (method: monthly reconciliation of field records vs CO log; baseline: run reconciliation on current project); change capture latency days (field record date vs log date); change cycle time (log timestamps; baseline: last project's log). +6 QUALITY / REWORK - first-time quality, CII rework research (2-6% direct cost where measured). Metrics: rework % of craft hours (method: dedicated rework cost code + reason coding, monthly; baseline: create the code, first month is baseline); first-pass yield at QC gates; out-of-rev incidents (QC walkdown coding). +7 KNOWLEDGE / FEEDBACK - CII lessons-learned, estimate feedback. Metrics: closeout compliance % within 60 days (method: closeout checklist audit; baseline: audit last 4-6 projects); % estimates referencing prior actuals (estimating file audit); punch closure median days (issue log timestamps; if untimestamped, that gap is the finding). +GATES: driver = business end in cost/schedule/quality/safety/predictability terms, never a tool. capability = function not product, no vendor names. metric = outcome ratio/rate, NEVER "implemented/adopted/rolled out/active users/have a tool". method = data source + comparison + cadence. baseline = a number or the named pre-pilot study that produces one (cheapest defensible: poll < record trace < log audit < 2-week coding < sampling study). Set targets after baseline, not before. Each field under ~30 words except method/baseline.`; + +function parseJson(text: string): Record { + const clean = String(text).replace(/```json|```/g, '').trim(); + const candidates = ['[', '{'].map((ch) => { + const i = clean.indexOf(ch); + return i === -1 ? Infinity : i; + }); + const start = Math.min(...candidates); + return JSON.parse(start === Infinity ? clean : clean.slice(start)); +} + +/** True when the AI feature is configured. Used to disable the button gracefully. */ +export function aiConfigured(): boolean { + return Boolean(env.ANTHROPIC_API_KEY); +} + +export async function draftBreadcrumb(prob: string, related: string[]): Promise { + const apiKey = env.ANTHROPIC_API_KEY; + if (!apiKey) throw new Error('AI is not configured on this server (missing ANTHROPIC_API_KEY).'); + if (!prob.trim()) throw new Error('Write the problem statement first.'); + + const client = new Anthropic({ apiKey }); + const rel = related.length + ? `\nRelated issues in this cluster:\n${related.map((r) => '- ' + r).join('\n')}` + : ''; + const prompt = `You populate breadcrumb trails for construction field problems at an industrial I&C contractor, using best-in-class execution methodology.\n\n${METHOD_KB}\n\nProblem statement:\n${prob}${rel}\n\nClassify the problem into one domain above, then draft the five fields per that domain's standard metrics, methods, and baselines, obeying every GATE. Respond ONLY with a JSON object, no prose, no markdown fences:\n{"domain":"...","driver":"...","cap":"...","metric":"...","method":"...","baseline":"..."}`; + + const message = await client.messages.create({ + model: env.ANTHROPIC_MODEL || 'claude-sonnet-5', + max_tokens: 1000, + messages: [{ role: 'user', content: prompt }] + }); + + const text = message.content + .filter((block): block is Anthropic.TextBlock => block.type === 'text') + .map((block) => block.text) + .join('\n'); + + const obj = parseJson(text); + for (const f of REQUIRED_FIELDS) { + if (!obj[f]) throw new Error('AI returned an incomplete draft; try again.'); + } + if (ADOPTION_WORDS.test(String(obj.metric))) { + throw new Error('Draft failed the adoption gate; try again.'); + } + + return { + domain: String(obj.domain ?? 'methodology'), + driver: String(obj.driver), + cap: String(obj.cap), + metric: String(obj.metric), + method: String(obj.method), + baseline: String(obj.baseline) + }; +} diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts new file mode 100644 index 0000000..a80eb94 --- /dev/null +++ b/src/lib/server/db/index.ts @@ -0,0 +1,27 @@ +// Postgres connection pool. Created lazily on first use so the module can be +// imported during build/analysis without DATABASE_URL being present. +import pg from 'pg'; +import { env } from '$env/dynamic/private'; + +let pool: pg.Pool | null = null; + +function getPool(): pg.Pool { + if (pool) return pool; + const connectionString = env.DATABASE_URL; + if (!connectionString) { + // Fail loudly on first query rather than mysteriously later. + throw new Error( + 'DATABASE_URL is not set. Copy .env.example to .env (or set it in docker-compose) and try again.' + ); + } + pool = new pg.Pool({ connectionString }); + return pool; +} + +/** Small helper so call sites read as `query(sql, params)`. */ +export function query( + text: string, + params?: unknown[] +): Promise> { + return getPool().query(text, params); +} diff --git a/src/lib/server/db/workspaces.ts b/src/lib/server/db/workspaces.ts new file mode 100644 index 0000000..bcbf43e --- /dev/null +++ b/src/lib/server/db/workspaces.ts @@ -0,0 +1,107 @@ +// Repository for reading and writing tool workspaces. +// +// Concurrency model: optimistic locking on an integer `version`. A client loads +// a workspace (getting its version), edits locally, then saves with that same +// version as `baseVersion`. If nobody else saved in between, the row's version +// still matches and the write wins (bumping version). If someone else saved +// first, the versions differ, no row updates, and we report a conflict so the +// client can reload instead of clobbering their teammate. + +import { query } from './index'; +import { defaultStateFor } from '$lib/defaults'; +import type { ToolId, WorkspaceEnvelope } from '$lib/types'; + +interface Row { + tool: ToolId; + name: string; + state: unknown; + version: number; + updated_at: Date; + updated_by: string | null; +} + +function toEnvelope(row: Row): WorkspaceEnvelope { + return { + tool: row.tool, + name: row.name, + state: row.state as T, + version: row.version, + updatedAt: row.updated_at.toISOString(), + updatedBy: row.updated_by + }; +} + +/** + * Load a workspace. If it does not exist yet, seed it from the tool's default + * baseline state so the first visitor sees a populated, useful tool. + */ +export async function getOrCreateWorkspace( + tool: ToolId, + name: string +): Promise> { + const existing = await query( + 'SELECT tool, name, state, version, updated_at, updated_by FROM workspaces WHERE tool = $1 AND name = $2', + [tool, name] + ); + if (existing.rows.length > 0) { + return toEnvelope(existing.rows[0]); + } + + const seed = defaultStateFor(tool); + // ON CONFLICT guards against a race where two first-visitors insert at once. + const inserted = await query( + `INSERT INTO workspaces (tool, name, state) + VALUES ($1, $2, $3) + ON CONFLICT (tool, name) DO UPDATE SET tool = EXCLUDED.tool + RETURNING tool, name, state, version, updated_at, updated_by`, + [tool, name, seed] + ); + return toEnvelope(inserted.rows[0]); +} + +export type SaveResult = + | { ok: true; workspace: WorkspaceEnvelope } + | { ok: false; conflict: WorkspaceEnvelope }; + +/** Save state with optimistic concurrency. */ +export async function saveWorkspace( + tool: ToolId, + name: string, + state: T, + baseVersion: number, + updatedBy: string | null +): Promise> { + const updated = await query( + `UPDATE workspaces + SET state = $3, version = version + 1, updated_at = now(), updated_by = $5 + WHERE tool = $1 AND name = $2 AND version = $4 + RETURNING tool, name, state, version, updated_at, updated_by`, + [tool, name, state, baseVersion, updatedBy] + ); + + if (updated.rows.length === 1) { + return { ok: true, workspace: toEnvelope(updated.rows[0]) }; + } + + // No row updated: either the workspace does not exist, or the version moved. + // Return the current server copy so the client can reconcile. + const current = await getOrCreateWorkspace(tool, name); + return { ok: false, conflict: current }; +} + +/** Reset a workspace back to the tool's baseline. */ +export async function resetWorkspace( + tool: ToolId, + name: string +): Promise> { + const seed = defaultStateFor(tool); + const reset = await query( + `INSERT INTO workspaces (tool, name, state) + VALUES ($1, $2, $3) + ON CONFLICT (tool, name) + DO UPDATE SET state = EXCLUDED.state, version = workspaces.version + 1, updated_at = now(), updated_by = NULL + RETURNING tool, name, state, version, updated_at, updated_by`, + [tool, name, seed] + ); + return toEnvelope(reset.rows[0]); +} diff --git a/src/lib/toast.svelte.ts b/src/lib/toast.svelte.ts new file mode 100644 index 0000000..0435a5a --- /dev/null +++ b/src/lib/toast.svelte.ts @@ -0,0 +1,15 @@ +// Tiny shared toast controller. Import `toast` anywhere and call toast.show('...'). +class ToastController { + message = $state(''); + visible = $state(false); + #timer: ReturnType | null = null; + + show(msg: string, ms = 2200): void { + this.message = msg; + this.visible = true; + if (this.#timer) clearTimeout(this.#timer); + this.#timer = setTimeout(() => (this.visible = false), ms); + } +} + +export const toast = new ToastController(); diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..9d55d47 --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,115 @@ +// Shared data types for both meeting tools. These describe exactly what gets +// stored as the JSON "state" of a workspace in the database. + +export type ToolId = 'workshop' | 'scope-lock'; + +// ---------- Field Problem Workshop ---------- + +export type ProblemType = 'Problem' | 'Question' | 'Idea'; + +export interface Problem { + id: string; + t: string; // the problem text + who: string; // who raised it + cluster: string; // cluster id, or '' if unclustered + type: ProblemType; + sugg: boolean; // came from theme-analysis suggestions +} + +export interface Cluster { + id: string; + name: string; + root: string; // root problem statement + ws: string; // workstream label + open: boolean; + dots: number; // dot-voting count + promoted: boolean; +} + +export type BreadcrumbType = 'Outcome' | 'Adoption'; + +export interface Breadcrumb { + id: string; + example: boolean; + type: BreadcrumbType; + prob: string; + related: string[]; + driver: string; + cap: string; + metric: string; + method: string; + baseline: string; + target: string; +} + +export interface WorkshopAction { + id: string; + t: string; + owner: string; + due: string; + done: boolean; +} + +export interface WorkshopState { + clusters: Cluster[]; + problems: Problem[]; + breadcrumbs: Breadcrumb[]; + actions: WorkshopAction[]; +} + +// ---------- Scope Lock Suite ---------- + +export type ScopeBucket = '' | 'mvp' | 'later' | 'out' | 'park'; + +export interface ScopeItem { + id: string; + t: string; + b: ScopeBucket; // bucket + lock: boolean; // locked pre-decision +} + +export interface RankItem { + id: string; + t: string; + n: string; // note +} + +export type DecisionStatus = 'Locked' | 'Provisional' | 'Revisit'; + +export interface Decision { + text: string; + type: string; // Scope / Priority / Technical / Process / ... + owner: string; + status: DecisionStatus; + why: string; // rationale + trade: string; // trade-off accepted + at: string; // time logged, e.g. "02:15 PM" +} + +export interface ReadyItem { + id: string; + g: string; // group (Data / Platform / Field / People / Governance) + t: string; // item text + w: string; // "what it means" note + o: string; // owner + s: number; // status (0 = not started, etc.) +} + +export interface ScopeLockState { + scope: ScopeItem[]; + rank: RankItem[]; + cutAfter: number; + decisions: Decision[]; + ready: ReadyItem[]; +} + +// ---------- Workspace envelope (what the API returns) ---------- + +export interface WorkspaceEnvelope { + tool: ToolId; + name: string; + state: T; + version: number; + updatedAt: string; + updatedBy: string | null; +} diff --git a/src/lib/workspace.svelte.ts b/src/lib/workspace.svelte.ts new file mode 100644 index 0000000..299ae18 --- /dev/null +++ b/src/lib/workspace.svelte.ts @@ -0,0 +1,138 @@ +// Reactive client-side workspace synchronizer. +// +// Usage in a component: +// const ws = new Workspace('workshop', 'default'); +// onMount(() => ws.init()); +// ... read/write ws.state ...; call ws.touch() after any mutation. +// +// It debounces saves, uses the version returned by the server for optimistic +// concurrency, and polls so another person's saves appear here too. + +import type { ToolId, WorkspaceEnvelope } from './types'; + +export type SyncStatus = 'loading' | 'idle' | 'saving' | 'saved' | 'conflict' | 'error'; + +const SAVE_DEBOUNCE_MS = 700; +const POLL_INTERVAL_MS = 6000; + +export class Workspace { + tool: ToolId; + name: string; + + state = $state(null); + version = $state(0); + status = $state('loading'); + updatedAt = $state(null); + message = $state(''); + + #saveTimer: ReturnType | null = null; + #pollTimer: ReturnType | null = null; + #dirty = false; // local edits not yet saved + #saving = false; + + constructor(tool: ToolId, name = 'default') { + this.tool = tool; + this.name = name; + } + + get base(): string { + return `/api/workspaces/${this.tool}/${encodeURIComponent(this.name)}`; + } + + async init(): Promise { + await this.load(); + this.#pollTimer = setInterval(() => this.#poll(), POLL_INTERVAL_MS); + } + + destroy(): void { + if (this.#saveTimer) clearTimeout(this.#saveTimer); + if (this.#pollTimer) clearInterval(this.#pollTimer); + } + + async load(): Promise { + this.status = 'loading'; + try { + const res = await fetch(this.base); + if (!res.ok) throw new Error(`load failed (${res.status})`); + const env = (await res.json()) as WorkspaceEnvelope; + this.#adopt(env); + this.status = 'idle'; + } catch (e) { + this.status = 'error'; + this.message = e instanceof Error ? e.message : 'Could not load'; + } + } + + #adopt(env: WorkspaceEnvelope): void { + this.state = env.state; + this.version = env.version; + this.updatedAt = env.updatedAt; + this.#dirty = false; + } + + /** Call after mutating `state`. Schedules a debounced save. */ + touch(): void { + this.#dirty = true; + this.status = 'saving'; + if (this.#saveTimer) clearTimeout(this.#saveTimer); + this.#saveTimer = setTimeout(() => this.save(), SAVE_DEBOUNCE_MS); + } + + async save(): Promise { + if (this.#saving || this.state === null) return; + this.#saving = true; + try { + const res = await fetch(this.base, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ state: $state.snapshot(this.state), baseVersion: this.version }) + }); + + if (res.status === 409) { + // Someone else saved first. Adopt their copy so we don't clobber it. + const { current } = (await res.json()) as { current: WorkspaceEnvelope }; + this.#adopt(current); + this.status = 'conflict'; + this.message = 'A teammate updated this workspace; their changes were loaded.'; + return; + } + if (!res.ok) throw new Error(`save failed (${res.status})`); + + const env = (await res.json()) as WorkspaceEnvelope; + this.version = env.version; + this.updatedAt = env.updatedAt; + this.#dirty = false; + this.status = 'saved'; + } catch (e) { + this.status = 'error'; + this.message = e instanceof Error ? e.message : 'Could not save'; + } finally { + this.#saving = false; + } + } + + async reset(): Promise { + const res = await fetch(`${this.base}/reset`, { method: 'POST' }); + if (res.ok) { + this.#adopt((await res.json()) as WorkspaceEnvelope); + this.status = 'idle'; + } + } + + async #poll(): Promise { + // Don't stomp on unsaved local edits or an in-flight save. + if (this.#dirty || this.#saving) return; + try { + const res = await fetch(this.base); + if (!res.ok) return; + const env = (await res.json()) as WorkspaceEnvelope; + if (env.version > this.version) this.#adopt(env); + } catch { + // Ignore transient poll failures. + } + } +} + +export function uid(): string { + return 'x' + Math.random().toString(36).slice(2, 9); +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..9b776b7 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,6 @@ + + +{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..3b28bca --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,108 @@ + + +Project SDE | Meeting Toolkit + +
+
Project SDE | Meeting Toolkit
+
+
Shared workspace · everyone sees the same data
+
+ +
+
+

Meeting Toolkit

+

+ Pick a tool. Everything you enter is saved to the shared server automatically, so anyone + who opens the same link works from the same data. +

+
+ +
+ {#each TOOLS as tool (tool.id)} + +

{tool.title}

+

{tool.blurb}

+ Open → +
+ {/each} +
+
+ + diff --git a/src/routes/api/ai/draft/+server.ts b/src/routes/api/ai/draft/+server.ts new file mode 100644 index 0000000..7bbad6f --- /dev/null +++ b/src/routes/api/ai/draft/+server.ts @@ -0,0 +1,28 @@ +import { error, json } from '@sveltejs/kit'; +import { draftBreadcrumb } from '$lib/server/ai'; +import type { RequestHandler } from './$types'; + +/** + * POST /api/ai/draft — draft breadcrumb fields from a problem statement. + * Body: { prob: string, related?: string[] } + */ +export const POST: RequestHandler = async ({ request }) => { + let body: { prob?: string; related?: string[] }; + try { + body = await request.json(); + } catch { + throw error(400, 'Body must be JSON'); + } + + const prob = String(body.prob ?? '').trim(); + const related = Array.isArray(body.related) ? body.related.map(String) : []; + + try { + const draft = await draftBreadcrumb(prob, related); + return json(draft); + } catch (e) { + const message = e instanceof Error ? e.message : 'AI draft failed'; + // 422: the request was well-formed but the AI step could not complete. + throw error(422, message); + } +}; diff --git a/src/routes/api/workspaces/[tool]/[name]/+server.ts b/src/routes/api/workspaces/[tool]/[name]/+server.ts new file mode 100644 index 0000000..77c0c01 --- /dev/null +++ b/src/routes/api/workspaces/[tool]/[name]/+server.ts @@ -0,0 +1,42 @@ +import { error, json } from '@sveltejs/kit'; +import { isToolId } from '$lib/defaults'; +import { getOrCreateWorkspace, saveWorkspace } from '$lib/server/db/workspaces'; +import type { RequestHandler } from './$types'; + +/** GET /api/workspaces/:tool/:name — load (creating from baseline if new). */ +export const GET: RequestHandler = async ({ params }) => { + if (!isToolId(params.tool)) throw error(404, 'Unknown tool'); + const ws = await getOrCreateWorkspace(params.tool, params.name); + return json(ws); +}; + +/** + * PUT /api/workspaces/:tool/:name — save with optimistic concurrency. + * Body: { state, baseVersion, updatedBy? } + * 200 with the saved workspace, or 409 with the current server copy on conflict. + */ +export const PUT: RequestHandler = async ({ params, request }) => { + if (!isToolId(params.tool)) throw error(404, 'Unknown tool'); + + let body: { state?: unknown; baseVersion?: number; updatedBy?: string | null }; + try { + body = await request.json(); + } catch { + throw error(400, 'Body must be JSON'); + } + + if (body.state === undefined || body.state === null) throw error(400, 'Missing "state"'); + if (typeof body.baseVersion !== 'number') throw error(400, 'Missing numeric "baseVersion"'); + + const result = await saveWorkspace( + params.tool, + params.name, + body.state, + body.baseVersion, + body.updatedBy ?? null + ); + + if (result.ok) return json(result.workspace); + // Someone else saved first. Hand back the current copy so the client reconciles. + return json({ conflict: true, current: result.conflict }, { status: 409 }); +}; diff --git a/src/routes/api/workspaces/[tool]/[name]/reset/+server.ts b/src/routes/api/workspaces/[tool]/[name]/reset/+server.ts new file mode 100644 index 0000000..3fc67f3 --- /dev/null +++ b/src/routes/api/workspaces/[tool]/[name]/reset/+server.ts @@ -0,0 +1,11 @@ +import { error, json } from '@sveltejs/kit'; +import { isToolId } from '$lib/defaults'; +import { resetWorkspace } from '$lib/server/db/workspaces'; +import type { RequestHandler } from './$types'; + +/** POST /api/workspaces/:tool/:name/reset — reset to the tool's baseline. */ +export const POST: RequestHandler = async ({ params }) => { + if (!isToolId(params.tool)) throw error(404, 'Unknown tool'); + const ws = await resetWorkspace(params.tool, params.name); + return json(ws); +}; diff --git a/src/routes/scope-lock/+page.svelte b/src/routes/scope-lock/+page.svelte new file mode 100644 index 0000000..3a70d6c --- /dev/null +++ b/src/routes/scope-lock/+page.svelte @@ -0,0 +1,952 @@ + + +Project SDE | Scope Lock Meeting Suite + +
+
Project SDE | Scope Lock Meeting Suite
+
+ + + +
+ + + +{#if !s} +

Loading workspace…

+{:else} +
+ + {#if tab === 'board'} +
+

Scope Boundary Board

+

+ Every item gets a home before the meeting ends. In MVP means the 3 month construction + tracking build. Later Phase means committed but sequenced after MVP. Out means not part of + Project SDE. Parking Lot means undecided, and each parked item needs an owner and a date in + the Decision Registry. +

+
+ +
+

Unsorted

+
Work top to bottom. Assign each item with the buttons on its card.
+
+ {#each s.scope.filter((i) => !i.b) as item (item.id)} + {@render card(item)} + {:else} + All items sorted. Nice work. + {/each} +
+
+ e.key === 'Enter' && addScope()} /> + +
+
+ +
+ {#each BUCKETS as b (b.k)} +
+

{b.label}

{s.scope.filter((i) => i.b === b.k).length}
+
{b.sub}
+
+ {#each s.scope.filter((i) => i.b === b.k) as item (item.id)} + {@render card(item)} + {/each} +
+
+ {/each} +
+ {/if} + + + {#if tab === 'rank'} +
+

MVP Priority Ranker

+

Forced ranking. If the 3 month timeline slips, items below the cut line are the first to be descoped. Move items with the arrows until the room agrees on the order.

+
+
+ {#each s.rank as item, i (item.id)} + {#if i === s.cutAfter} +
+
DESCOPE LINE: below here goes first if the timeline slips
+ {/if} +
+
{i + 1}
+
{item.t}{#if item.n}
{item.n}
{/if}
+ + + +
+ {/each} +
+ Descope line sits after position {s.cutAfter}. + + +
+
+ e.key === 'Enter' && addRank()} /> + +
+
+ {/if} + + + {#if tab === 'registry'} +
+

Decision Registry

+

Log every scope decision as it is made, with the trade off the team accepted. Nothing stays verbal only. Locked means it does not get relitigated without a formal change.

+
+
+
+
+
+
+
+
+
+
+ + {#if s.decisions.length} + + + + {#each s.decisions as d, i (i)} + + + + + + + + + + + {/each} + +
#DecisionTypeOwnerRationaleTrade offStatus
{i + 1}{d.text}{d.type}{d.owner}{d.why}{d.trade}{d.status}
+ {:else} +
No decisions logged yet. The first one usually takes two minutes; the rest take thirty seconds.
+ {/if} + {/if} + + + {#if tab === 'ready'} +
+

Micron Pilot Readiness

+

What must be true at Micron before the pilot can start. Tap the status to cycle it. Anything Blocked at the end of the meeting needs a decision in the Registry.

+
+
+
+
{readyCounts[2]}Ready
+
{readyCounts[1]}In progress
+
{readyCounts[3]}Blocked
+
{readyCounts[0]}Not started
+
+ {#each readyGroups as g (g)} +
+

{g}

+ {#each s.ready.filter((i) => i.g === g) as item (item.id)} +
+
{item.t}{#if item.w}{item.w}{/if}
+ ws.touch()} /> + +
+ {/each} +
+ {/each} +
+ e.key === 'Enter' && addReady()} /> + +
+
+ {/if} +
+{/if} + +{#snippet card(item: ScopeLockState['scope'][number])} +
+
{item.t}{#if item.lock}Pre locked{/if}
+
+ {#each BUCKETS as b (b.k)} + + {/each} + {#if !item.lock}{/if} +
+
+{/snippet} + + + + diff --git a/src/routes/workshop/+page.svelte b/src/routes/workshop/+page.svelte new file mode 100644 index 0000000..5fe9ea2 --- /dev/null +++ b/src/routes/workshop/+page.svelte @@ -0,0 +1,1236 @@ + + +Project SDE | Field Problem Workshop v5 + +
+
Project SDE | Field Problem Workshop v5
+
+ + + + + + +
+ + + +{#if !s} +

Loading workspace…

+{:else} +
+ + {#if tab === 'map'} +
+

Problem Map

+

Capture at the top; new items land in the red tray until attached to a root problem. Each cluster is a root problem and the issues under it are the evidence. Vote with dots directly on the cluster headers, then promote the winners to Breadcrumbs.

+
+
+ e.key === 'Enter' && addProblem()} /> + + + +
+ +
+

Unclustered ({unclustered.length})

+ {#if unclustered.length} + {#each unclustered as p (p.id)} +
+ {p.type} +
{p.t}
+ + +
+ {/each} + {:else} +
Everything is attached to a root problem.
+ {/if} +
+ +
+ {#each s.clusters as c (c.id)} + {@const mem = membersOf(c.id)} + {@const rank = rankedIds.indexOf(c.id)} +
+
+ + + {#if c.promoted} + + {:else} + + {/if} +
+ + {c.dots} + +
+ +
+ {#if c.open} +
+
+ + +
+
+ {#if mem.length} + {#each mem as p (p.id)} + {#if editing[p.id]} +
+ + + +
+ {:else} +
+ {p.type} +
{p.t} {#if p.sugg}Suggested{/if} ({p.sugg ? 'theme analysis' : p.who})
+ + + +
+ {/if} + {/each} + {:else} +
No issues attached yet.
+ {/if} +
+ {#if ghostsFor(c.id).length} +
+
Also consider (from theme analysis):
+ {#each ghostsFor(c.id) as sg (sg.idx)} +
+
{sg.t}
+ +
+ {/each} +
+ {/if} +
+ e.key === 'Enter' && addIssueToCluster(c.id)} /> + + +
+
+ {/if} +
+ {/each} +
+ +
+ e.key === 'Enter' && addCluster()} /> + +
+ {/if} + + + {#if tab === 'bc'} +
+

Breadcrumbs

+

Root problem to value driver to capability to measurable outcome. A completed breadcrumb IS a pilot success criterion; the export builds the criteria table from every complete card. AI draft fills the fields from construction execution methodology (EVM, CII AWP, Last Planner, first-time quality); the room reviews and edits.

+
+
+
{nDone}Complete (= criteria)
+
0}>{s.breadcrumbs.length - nDone}Incomplete
+
{nOut}Outcome
+
{s.breadcrumbs.length - nOut}Adoption
+
+ + {#if !s.breadcrumbs.length} +
Promote root problems from the map, or add a blank breadcrumb.
+ {:else} + {#each s.breadcrumbs as b (b.id)} +
+
+
+

1Root problem statement {#if b.example}Worked example{/if}

+ +
+
+

2Value driver

+ +
+
+

3Solution capability

+ +
+
+ {#if b.related.length} + + {/if} +
+
4Measurable outcome (a complete card is a pilot success criterion)
+

Metric

+

Method

+

Baseline

+

Target (optional, set after baseline)

+
+
+ +
+ {#each REQ as f} + {@const ok = String(b[f]).trim().length > 0} + {ok ? '✓' : '○'} {f === 'cap' ? 'capability' : f} + {/each} +
+ {#if b.type === 'Outcome' && ADOPTION_WORDS.test(b.metric)} + Metric reads as adoption. Name the outcome the tool should move, or tag this card Adoption. + {/if} + + +
+
+ {/each} + {/if} +
+ {/if} + + + {#if tab === 'act'} +
+

Actions

+

Every open item gets an owner and a date. Scan adds an action for each incomplete breadcrumb, each cluster missing a root statement, and any unclustered issues.

+
+
+ {#if !s.actions.length} +
No actions. Use Scan or add one manually.
+ {:else} + {#each s.actions as a (a.id)} +
+ +
{a.t}
+ + ws.touch()} /> + +
+ {/each} + {/if} +
+ e.key === 'Enter' && addAct()} /> + +
+ {/if} +
+{/if} + + + + diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..0733b4b --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..31528a9 --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,13 @@ +import adapter from '@sveltejs/adapter-node'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + // Runs as a standalone Node server (build/index.js) — ideal for Docker. + adapter: adapter() + } +}; + +export default config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a8f10c8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..bbf8c7d --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()] +});