From cef29d40bd2168a6b28f59f45abc4f6e8ff6b0f0 Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Wed, 20 May 2026 12:11:27 +0800 Subject: [PATCH] Initial commit: Inner OS v1 Full-stack inner development operating system built on Next.js 16 + Prisma 7 + PostgreSQL + OpenAI + Clerk. Generates personalized daily mental training tasks, collects reflection, updates user state, and provides internal reward feedback. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 43 + AGENTS.md | 5 + CLAUDE.md | 28 + DESIGN.md | 128 +++ README.md | 36 + TODOS.md | 43 + bun.lock | 983 +++++++++++++++++++++ data/task-templates.json | 107 +++ eslint.config.mjs | 18 + next.config.ts | 7 + package.json | 32 + postcss.config.mjs | 7 + prisma.config.ts | 14 + prisma/schema.prisma | 126 +++ public/file.svg | 1 + public/globe.svg | 1 + public/next.svg | 1 + public/vercel.svg | 1 + public/window.svg | 1 + src/app/api/cron/weekly-summaries/route.ts | 40 + src/app/api/rewards/route.ts | 25 + src/app/api/session/complete/route.ts | 167 ++++ src/app/api/summary/weekly/route.ts | 24 + src/app/api/task/daily/route.ts | 49 + src/app/api/task/generate/route.ts | 27 + src/app/api/user/onboarding/route.ts | 32 + src/app/api/user/state/route.ts | 71 ++ src/app/favicon.ico | Bin 0 -> 25931 bytes src/app/globals.css | 46 + src/app/layout.tsx | 44 + src/app/page.tsx | 265 ++++++ src/app/practice/page.tsx | 216 +++++ src/app/progress/page.tsx | 130 +++ src/app/reflect/page.tsx | 198 +++++ src/app/rewards/page.tsx | 171 ++++ src/app/settings/page.tsx | 145 +++ src/app/summary/page.tsx | 82 ++ src/components/StageBadge.tsx | 20 + src/components/StatBar.tsx | 44 + src/components/TabBar.tsx | 149 ++++ src/lib/engines/progress-tracker.ts | 185 ++++ src/lib/engines/reflection-analyzer.ts | 202 +++++ src/lib/engines/reward-generator.ts | 153 ++++ src/lib/engines/summary-generator.ts | 191 ++++ src/lib/engines/task-selector.ts | 326 +++++++ src/lib/prisma.ts | 19 + src/lib/safety.ts | 64 ++ src/proxy.ts | 22 + tsconfig.json | 34 + 49 files changed, 4723 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 DESIGN.md create mode 100644 README.md create mode 100644 TODOS.md create mode 100644 bun.lock create mode 100644 data/task-templates.json create mode 100644 eslint.config.mjs create mode 100644 next.config.ts create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 prisma.config.ts create mode 100644 prisma/schema.prisma create mode 100644 public/file.svg create mode 100644 public/globe.svg create mode 100644 public/next.svg create mode 100644 public/vercel.svg create mode 100644 public/window.svg create mode 100644 src/app/api/cron/weekly-summaries/route.ts create mode 100644 src/app/api/rewards/route.ts create mode 100644 src/app/api/session/complete/route.ts create mode 100644 src/app/api/summary/weekly/route.ts create mode 100644 src/app/api/task/daily/route.ts create mode 100644 src/app/api/task/generate/route.ts create mode 100644 src/app/api/user/onboarding/route.ts create mode 100644 src/app/api/user/state/route.ts create mode 100644 src/app/favicon.ico create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/page.tsx create mode 100644 src/app/practice/page.tsx create mode 100644 src/app/progress/page.tsx create mode 100644 src/app/reflect/page.tsx create mode 100644 src/app/rewards/page.tsx create mode 100644 src/app/settings/page.tsx create mode 100644 src/app/summary/page.tsx create mode 100644 src/components/StageBadge.tsx create mode 100644 src/components/StatBar.tsx create mode 100644 src/components/TabBar.tsx create mode 100644 src/lib/engines/progress-tracker.ts create mode 100644 src/lib/engines/reflection-analyzer.ts create mode 100644 src/lib/engines/reward-generator.ts create mode 100644 src/lib/engines/summary-generator.ts create mode 100644 src/lib/engines/task-selector.ts create mode 100644 src/lib/prisma.ts create mode 100644 src/lib/safety.ts create mode 100644 src/proxy.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f390d12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +/src/generated/prisma diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8bd0e39 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..da917b6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,28 @@ +# Inner OS — Claude Code Configuration + +## Project overview +Inner OS v1 is an AI-driven inner development operating system. Next.js monolith + Prisma + PostgreSQL + OpenAI. Generates one personalized daily mental training task, explains why it's chosen, collects reflection, updates user state, and provides internal (non-competitive) reward feedback. + +## Skill routing + +When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill. + +Key routing rules: +- Product ideas/brainstorming → invoke /office-hours +- Strategy/scope → invoke /plan-ceo-review +- Architecture → invoke /plan-eng-review +- Design system/plan review → invoke /design-consultation or /plan-design-review +- Full review pipeline → invoke /autoplan +- Bugs/errors → invoke /investigate +- QA/testing site behavior → invoke /qa or /qa-only +- Code review/diff check → invoke /review +- Visual polish → invoke /design-review +- Ship/deploy/PR → invoke /ship or /land-and-deploy +- Save progress → invoke /context-save +- Resume context → invoke /context-restore + +## Design System +Always read DESIGN.md before making any visual or UI decisions. +All font choices, colors, spacing, and aesthetic direction are defined there. +Do not deviate without explicit user approval. +In QA mode, flag any code that doesn't match DESIGN.md. diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..c8b2568 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,128 @@ +# Design System — Inner OS + +## Product Context +- **What this is:** AI-driven inner development operating system — generates one personalized daily mental training task, explains why it's chosen, collects post-session reflection, updates user state, and delivers internal reward feedback. +- **Who it's for:** Solo inner development practitioners who want structured mental training without a human coach. +- **Space/industry:** Mental wellness / inner development (adjacent to Headspace, Calm, Waking Up) +- **Project type:** Web app (mobile-first, journal-width constrained) + +## Aesthetic Direction +- **Direction:** Editorial/Organic hybrid — typographic warmth with natural earth tones +- **Decoration level:** Intentional — subtle warmth cues (cream background, soft rounded corners) without decorative blobs or gradients. The "decoration" IS the typography and whitespace. +- **Mood:** Calm companion. The UI should feel like a well-printed private journal, not a SaaS dashboard. Paper texture, ink-color text, breathing room. The app recedes; the practice is center stage. +- **Memorable thing:** "It feels like a private journal, not an app." +- **Reference sites:** None (greenfield design system) + +## Typography +- **Display/Headings:** Fraunces (serif, wght 300-700, opsz 16-48) — Literary, warm serif with variable optical size. Feels like a book, not a browser. Soft curves and humanist proportions serve the private journal metaphor. +- **Body:** DM Sans (sans-serif, wght 400-500-700) — Clean geometric sans with humanist warmth. High x-height for readability at 16px. +- **UI/Labels:** DM Sans (500 weight) — Same family, slightly bolder. +- **Data/Tables:** DM Sans (tabular-nums enabled) — Built-in tabular figures for progress charts and metrics. +- **Code:** JetBrains Mono (developer tooling only, never user-facing) +- **Loading:** Google Fonts CDN with `font-display: swap` +- **Scale:** + +| Level | Size / Line-height | Use | +|-------|-------------------|-----| +| xs | 12px / 1.5 (18px) | Captions, timestamps | +| sm | 14px / 1.5 (21px) | Secondary text, labels | +| base | 16px / 1.6 (25.6px) | Body text | +| md | 18px / 1.5 (27px) | Card headings, emphasized body | +| lg | 22px / 1.4 (30.8px) | Section headings | +| xl | 28px / 1.3 (36.4px) | Page titles | +| 2xl | 36px / 1.2 (43.2px) | Hero/day heading | + +## Color +- **Approach:** Restrained — one accent (amber), one secondary (sage), warm neutrals. Color is rare and meaningful — it signals action, progress, or reward, never decoration. + +### Light Mode +| Token | Hex | Role | +|-------|-----|------| +| Primary/Accent | `#C8834A` (Amber) | CTAs, active states, streak indicators | +| Secondary | `#7A9A8B` (Sage) | Success states, progress fill, calm indicators | +| Background | `#FAF7F2` (Cream) | Page background | +| Surface | `#F3EFE8` (Sand) | Cards, elevated surfaces | +| Border | `#E6E0D4` (Stone) | Dividers, input borders | +| Text Primary | `#2C2416` (Ink) | Body text, headings | +| Text Secondary | `#6B5E4A` (Muted ink) | Captions, secondary labels | +| Text Tertiary | `#9B8E7A` (Faded ink) | Placeholders, disabled | + +### Semantic +| Token | Hex | Use | +|-------|-----|-----| +| Success | `#7A9A8B` (Sage) | Completion confirmations, progress | +| Warning | `#C8834A` (Amber) | Streak-at-risk, attention needed | +| Error | `#C45A4A` | Session expired, validation errors | +| Info | `#5A8AB5` | Neutral status, tips | + +### Dark Mode +- **Strategy:** Redesign surfaces, reduce saturation 15%. Warm dark background instead of cool dark. Amber and sage stay — they read warm against dark. +- **Tokens:** + +| Token | Hex | Role | +|-------|-----|------| +| Background | `#1E1A14` (Night ink) | Page background | +| Surface | `#292418` (Dark sand) | Cards, elevated surfaces | +| Border | `#3D3528` (Dark stone) | Dividers, input borders | +| Text Primary | `#F3EFE8` (Warm sand) | Body text, headings | +| Text Secondary | `#B8A892` (Muted cream) | Captions, secondary labels | +| Text Tertiary | `#8A7D6B` | Placeholders, disabled | + +## Spacing +- **Base unit:** 8px +- **Density:** Comfortable + +| Token | Value | Use | +|-------|-------|-----| +| 2xs | 4px | Icon-text gap, inline spacing | +| xs | 8px | Tight gaps, chip padding | +| sm | 12px | Card padding (compact) | +| md | 16px | Standard padding, card gaps | +| lg | 24px | Section gap, card padding (comfortable) | +| xl | 32px | Page-level spacing | +| 2xl | 48px | Major section divisions | +| 3xl | 64px | Top/bottom page breathing room | + +## Shape +- **Border radius (hierarchical):** + +| Token | Value | Use | +|-------|-------|-----| +| sm | 6px | Inputs, buttons | +| md | 12px | Cards, modals | +| lg | 16px | Large surfaces | +| full | 9999px | Pills, badges, stage indicators | + +## Layout +- **Approach:** Hybrid — single-column journal-width (640px max, centered) for reading/reflection pages; grid-disciplined for progress charts +- **Grid:** 1 column (mobile), centered 640px (desktop) +- **Max content width:** 640px +- **Navigation:** Bottom tab bar, 4 tabs (Today, Progress, Rewards, Settings), fixed position, 56px height, 44px minimum touch targets + +## Motion +- **Approach:** Intentional — transitions that aid comprehension without calling attention +- **Easing:** enter(ease-out), exit(ease-in), move(ease-in-out) +- **Duration:** + - Micro: 150ms (button hover) + - Short: 200ms (page transitions, fade + 4px Y shift) + - Medium: 400ms (reward animations, scale + fade) + - Long: 600ms (progress bar fill) + +## Accessibility +- **Baseline:** WCAG AA +- **Touch targets:** 44px minimum +- **Contrast:** All body text meets 4.5:1 against background +- **Keyboard:** All interactive elements keyboard-navigable +- **Focus:** Visible focus ring (amber outline, 2px) on all interactive elements +- **Screen readers:** ARIA landmarks on all pages (banner, main, navigation, complementary) + +## Decisions Log +| Date | Decision | Rationale | +|------|----------|-----------| +| 2026-05-20 | Initial design system created | Created by /design-consultation based on CEO plan, eng review, and 10 design review decisions | +| 2026-05-20 | Practice journal aesthetic | User's memorable thing: "It feels like a private journal, not an app" | +| 2026-05-20 | Fraunces + DM Sans typography | Literary serif for journal feel; clean sans for readability | +| 2026-05-20 | Cream/Amber/Sage/Ink palette | Warm, grounded, non-clinical colors that serve the journal metaphor | +| 2026-05-20 | Journal-width max 640px centered | Matches reading column width; mobile-first, not responsive-gimmick | +| 2026-05-20 | Bottom tab bar navigation | Standard mobile pattern, no learning curve, 4 primary destinations | +| 2026-05-20 | Calm companion emotional tone | Warm but not saccharine, grounded but not clinical | diff --git a/README.md b/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/TODOS.md b/TODOS.md new file mode 100644 index 0000000..1f17b9a --- /dev/null +++ b/TODOS.md @@ -0,0 +1,43 @@ +# Inner OS — Deferred Work + +## From /plan-eng-review (2026-05-20) + +### T1: Define state capture mechanism +**What:** Specify the UX and formulas for capturing attention_level, emotion_stability, and stress_level. +**Why:** These are inputs to task generation, but no data entry path exists. Session reflection has focus_score and emotion_score (1-5 user self-report), but no mapping to the state model. +**Context:** The session reflection form needs clear self-report questions ("How focused were you? 1-5", "How emotionally balanced? 1-5"). Map formulas: attention_delta = (focus_score - 3) * 5, emotion_delta = (emotion_score - 3) * 5. For the empty-reflection case (user writes nothing), default deltas to 0 (no change). +**Effort:** S (human: ~3h / CC: ~15min) +**Priority:** P1 +**Depends on:** Session reflection form design + +### T2: Add LLM output content safety +**What:** Add a lightweight safety layer on LLM-generated tasks before showing them to users. +**Why:** GPT-4o-mini could generate harmful suggestions (extreme practices, dangerous advice). A single bad output is a reputational event. +**Context:** Implement a keyword blocklist (self-harm, extreme behaviors, medical advice) and an optional LLM self-review prompt ("Does this task contain harmful or dangerous suggestions? Reply yes/no."). If flagged, fall back to a template task. +**Effort:** S (human: ~2h / CC: ~10min) +**Priority:** P1 +**Depends on:** Task generation pipeline + +### T3: Define stage advancement rules per level +**What:** Define concrete thresholds for each stage transition (0→1, 1→2, 2→3, 3→4, 4→5). +**Why:** The LLM prompt needs current stage to set task difficulty. Without per-level rules, advancement is arbitrary. +**Context:** Proposed rules: +- Stage 0→1: 7 completed sessions (any consistency) +- Stage 1→2: 14 completed sessions + consistency >= 60 +- Stage 2→3: 30 completed sessions + consistency >= 80 + stability >= 60 +- Stage 3→4: 60 completed sessions + consistency >= 90 + stability >= 70 +- Stage 4→5: 120 completed sessions + consistency >= 95 + stability >= 80 +**Effort:** S (human: ~1h / CC: ~10min) +**Priority:** P2 +**Depends on:** — + +### T4: Add Clerk user deletion webhook handling +**What:** Handle Clerk user.deleted webhook to cascade-delete user data, plus a periodic cleanup job for orphaned rows. +**Why:** Clerk webhooks are at-least-once (not exactly-once). If delivery fails, UserState/Sessions/Tasks/RewardEvents become orphaned with no owner. +**Context:** Add `onDelete: Cascade` on User→UserState Prisma relation. Implement Clerk webhook handler at `/api/webhooks/clerk`. Add a weekly cleanup job that deletes rows where user_id doesn't exist in Clerk (query Clerk API). +**Effort:** M (human: ~4h / CC: ~15min) +**Priority:** P1 +**Depends on:** Clerk integration + +## From /plan-ceo-review (2026-05-20) +- (all scope items accepted as "Build now" — nothing deferred from CEO review) diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..c090786 --- /dev/null +++ b/bun.lock @@ -0,0 +1,983 @@ +{ + "lockfileVersion": 1, + "configVersion": 0, + "workspaces": { + "": { + "name": "inner-os", + "dependencies": { + "@clerk/nextjs": "^7.3.7", + "@prisma/adapter-pg": "^7.8.0", + "@prisma/client": "^7.8.0", + "next": "16.2.6", + "openai": "^6.38.0", + "react": "19.2.4", + "react-dom": "19.2.4", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20.19.41", + "@types/react": "^19", + "@types/react-dom": "^19", + "dotenv": "^17.4.2", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "prisma": "^7.8.0", + "tailwindcss": "^4", + "typescript": "^5", + }, + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, ""], + + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, ""], + + "@babel/compat-data": ["@babel/compat-data@7.29.3", "", {}, ""], + + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, ""], + + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, ""], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, ""], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, ""], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, ""], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, ""], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, ""], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, ""], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, ""], + + "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, ""], + + "@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" } }, ""], + + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, ""], + + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, ""], + + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, ""], + + "@clerk/backend": ["@clerk/backend@3.4.11", "", { "dependencies": { "@clerk/shared": "^4.12.2", "standardwebhooks": "^1.0.0", "tslib": "2.8.1" } }, "sha512-WT+4FrcMMofxe+irQYUkawoRWaHHXT9/wiRYhRbSb30cOPjN95ViydqPhAyp8ZWAHInHg4mILynQQRJH14SKZQ=="], + + "@clerk/nextjs": ["@clerk/nextjs@7.3.7", "", { "dependencies": { "@clerk/backend": "^3.4.11", "@clerk/react": "^6.6.6", "@clerk/shared": "^4.12.2", "server-only": "0.0.1", "tslib": "2.8.1" }, "peerDependencies": { "next": "^15.2.8 || ^15.3.8 || ^15.4.10 || ^15.5.9 || ^15.6.0-0 || ^16.0.10 || ^16.1.0-0", "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" } }, "sha512-CLG63zKPHditk52Z/+c6BJ47V1Q68ACjeps0xHDRW3X/Yv/FZdM+IUKu9Egb5PJ/TkRFxsI1nU5SOY2B8o/WxA=="], + + "@clerk/react": ["@clerk/react@6.6.6", "", { "dependencies": { "@clerk/shared": "^4.12.2", "tslib": "2.8.1" }, "peerDependencies": { "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" } }, "sha512-MVHLDZeGobSbGSZgAdb4G1BbB8ZU5XAmBBdIVLXiPOLEst2TYM5bP137WRA76y9GlmVmKYdKzph/TUi87P/JOA=="], + + "@clerk/shared": ["@clerk/shared@4.12.2", "", { "dependencies": { "@tanstack/query-core": "^5.100.6", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.9.0" }, "peerDependencies": { "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" } }, "sha512-jDkip8tKTzYz/cPKMCsjOoACH3Xh37zcbCrssMRTYOq3GZypIpZ6WAs4m4G82URL0WY+yz5frrHVjRrHyAb6LA=="], + + "@electric-sql/pglite": ["@electric-sql/pglite@0.4.1", "", {}, "sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q=="], + + "@electric-sql/pglite-socket": ["@electric-sql/pglite-socket@0.1.1", "", { "peerDependencies": { "@electric-sql/pglite": "0.4.1" }, "bin": { "pglite-server": "dist/scripts/server.js" } }, "sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw=="], + + "@electric-sql/pglite-tools": ["@electric-sql/pglite-tools@0.3.1", "", { "peerDependencies": { "@electric-sql/pglite": "0.4.1" } }, "sha512-C+T3oivmy9bpQvSxVqXA1UDY8cB9Eb9vZHL9zxWwEUfDixbXv4G3r2LjoTdR33LD8aomR3O9ZXEO3XEwr/cUCA=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, ""], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, ""], + + "@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, ""], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, ""], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, ""], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, ""], + + "@eslint/js": ["@eslint/js@9.39.4", "", {}, ""], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, ""], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, ""], + + "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], + + "@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, ""], + + "@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, ""], + + "@humanfs/types": ["@humanfs/types@0.15.0", "", {}, ""], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, ""], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, ""], + + "@img/colour": ["@img/colour@1.1.0", "", {}, ""], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, ""], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, ""], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, ""], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, ""], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, ""], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, ""], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, ""], + + "@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="], + + "@next/env": ["@next/env@16.2.6", "", {}, ""], + + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.2.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, ""], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.6", "", { "os": "darwin", "cpu": "arm64" }, ""], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.2.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.6", "", { "os": "win32", "cpu": "x64" }, "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, ""], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, ""], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, ""], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, ""], + + "@prisma/adapter-pg": ["@prisma/adapter-pg@7.8.0", "", { "dependencies": { "@prisma/driver-adapter-utils": "7.8.0", "@types/pg": "^8.16.0", "pg": "^8.16.3", "postgres-array": "3.0.4" } }, "sha512-ygb3UkerK3v8MDpXVgCISdRNDozpxh6+JVJgiIGbSr5KBgz10LLf5ejUskPGoXlsIjxsOu6nuy1JVQr2EKGSlg=="], + + "@prisma/client": ["@prisma/client@7.8.0", "", { "dependencies": { "@prisma/client-runtime-utils": "7.8.0" }, "peerDependencies": { "prisma": "*", "typescript": ">=5.4.0" } }, "sha512-HFp3Dawv/3sU3JtlPha90IB+48lS7zHiH4LKZPjmcE8YH5P9DOXGPvo8dqOtO7MqLDd1p2hOWMcFlRT1DMblHw=="], + + "@prisma/client-runtime-utils": ["@prisma/client-runtime-utils@7.8.0", "", {}, "sha512-5NQZztQ0oY/ADFkmd9gPuweH5A1/CCY8YQPorLLO0Mu6a87mY5gsnDkzmFmIHs9NFaLnZojzgddFVN4RpKYrdw=="], + + "@prisma/config": ["@prisma/config@7.8.0", "", { "dependencies": { "c12": "3.3.4", "deepmerge-ts": "7.1.5", "effect": "3.20.0", "empathic": "2.0.0" } }, "sha512-HFESzd9rx2ZQxlK+TL7tu1HPvCqrHiL6LCxYykI2c34mvaUuIVVl3lYuicJD/MNnzgPnyeBEMlK4WTomJCV5jw=="], + + "@prisma/debug": ["@prisma/debug@7.8.0", "", {}, "sha512-p+QZReysDUqXC+mk17q9a+Y/qzh4c2KYliDK30buYUyfrGeTGSyfmc0AIrJRhZJrLHhRiJa9Au/J72h3C+szvA=="], + + "@prisma/dev": ["@prisma/dev@0.24.3", "", { "dependencies": { "@electric-sql/pglite": "0.4.1", "@electric-sql/pglite-socket": "0.1.1", "@electric-sql/pglite-tools": "0.3.1", "@hono/node-server": "1.19.11", "@prisma/get-platform": "7.2.0", "@prisma/query-plan-executor": "7.2.0", "@prisma/streams-local": "0.1.2", "foreground-child": "3.3.1", "get-port-please": "3.2.0", "hono": "^4.12.8", "http-status-codes": "2.3.0", "pathe": "2.0.3", "proper-lockfile": "4.1.2", "remeda": "2.33.4", "std-env": "3.10.0", "valibot": "1.2.0", "zeptomatch": "2.1.0" } }, "sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg=="], + + "@prisma/driver-adapter-utils": ["@prisma/driver-adapter-utils@7.8.0", "", { "dependencies": { "@prisma/debug": "7.8.0" } }, "sha512-/Q13o0ZT0rjc1Xk0Q9KhZYwuq2EW/vSbWUBKfgEKkaCuB/Sg6bqnjmTZqC5cD4d6y1vfFAEwBRzfzoSMIVJ55A=="], + + "@prisma/engines": ["@prisma/engines@7.8.0", "", { "dependencies": { "@prisma/debug": "7.8.0", "@prisma/engines-version": "7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a", "@prisma/fetch-engine": "7.8.0", "@prisma/get-platform": "7.8.0" } }, "sha512-jx3rCnNNrt5uzbkKlegtQ2GZHxSlihMCzutgT/BP6UIDF1r9tDI39hV/0T/cHZgzJ3ELbuQPXlVZy+Y1n0pcgw=="], + + "@prisma/engines-version": ["@prisma/engines-version@7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a", "", {}, "sha512-fJPQxCkLgA5EayWaW8eArgCvjJ+N+Kz3VyeNKMEeYiQC4alNkxRKFVAGxv/ZUzuJISKqdw+zGeDbS6mn6RCPOA=="], + + "@prisma/fetch-engine": ["@prisma/fetch-engine@7.8.0", "", { "dependencies": { "@prisma/debug": "7.8.0", "@prisma/engines-version": "7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a", "@prisma/get-platform": "7.8.0" } }, "sha512-gwB0Euiz/DDRyxFRpLXYlK3RfaZUj1c5dAYMuhZYfApg7arknJlcb9bIsOHDppJmbqYaVA+yBIiFMDBfprsNPQ=="], + + "@prisma/get-platform": ["@prisma/get-platform@7.2.0", "", { "dependencies": { "@prisma/debug": "7.2.0" } }, "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA=="], + + "@prisma/query-plan-executor": ["@prisma/query-plan-executor@7.2.0", "", {}, "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ=="], + + "@prisma/streams-local": ["@prisma/streams-local@0.1.2", "", { "dependencies": { "ajv": "^8.12.0", "better-result": "^2.7.0", "env-paths": "^3.0.0", "proper-lockfile": "^4.1.2" } }, "sha512-l49yTxKKF2odFxaAXTmwmkBKL3+bVQ1tFOooGifu4xkdb9NMNLxHj27XAhTylWZod8I+ISGM5erU1xcl/oBCtg=="], + + "@prisma/studio-core": ["@prisma/studio-core@0.27.3", "", { "dependencies": { "@radix-ui/react-toggle": "1.1.10", "chart.js": "4.5.1" }, "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-AADjNFPdsrglxHQVTmHFqv6DuKQZ5WY4p5/gVFY017twvNrSwpLJ9lqUbYYxEu2W7nbvVxTZA8deJ8LseNALsw=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, ""], + + "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, ""], + + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, ""], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-darwin-arm64": "4.3.0" } }, ""], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, ""], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.0", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "postcss": "^8.5.10", "tailwindcss": "4.3.0" } }, ""], + + "@tanstack/query-core": ["@tanstack/query-core@5.100.11", "", {}, "sha512-lmE0994apShXPj8CUxgx4ch5yUJhE9k/+tVwihBvPOyerACWdBocfFg24t8+0RhtlTd7tEgchDkhlCxNssvDxw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, ""], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, ""], + + "@types/json5": ["@types/json5@0.0.29", "", {}, ""], + + "@types/node": ["@types/node@20.19.41", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ=="], + + "@types/pg": ["@types/pg@8.20.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow=="], + + "@types/react": ["@types/react@19.2.15", "", { "dependencies": { "csstype": "^3.2.2" } }, ""], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, ""], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.59.4", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.59.4", "@typescript-eslint/type-utils": "8.59.4", "@typescript-eslint/utils": "8.59.4", "@typescript-eslint/visitor-keys": "8.59.4", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.59.4", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.4", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.4", "@typescript-eslint/types": "8.59.4", "@typescript-eslint/typescript-estree": "8.59.4", "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.4", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.4", "@typescript-eslint/types": "^8.59.4", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.4", "", { "dependencies": { "@typescript-eslint/types": "8.59.4", "@typescript-eslint/visitor-keys": "8.59.4" } }, ""], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.4", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.4", "", { "dependencies": { "@typescript-eslint/types": "8.59.4", "@typescript-eslint/typescript-estree": "8.59.4", "@typescript-eslint/utils": "8.59.4", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.59.4", "", {}, ""], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.4", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.4", "@typescript-eslint/tsconfig-utils": "8.59.4", "@typescript-eslint/types": "8.59.4", "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.4", "@typescript-eslint/types": "8.59.4", "@typescript-eslint/typescript-estree": "8.59.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.4", "", { "dependencies": { "@typescript-eslint/types": "8.59.4", "eslint-visitor-keys": "^5.0.0" } }, ""], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.12.2", "", { "os": "darwin", "cpu": "arm64" }, ""], + + "acorn": ["acorn@8.16.0", "", { "bin": "bin/acorn" }, ""], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, ""], + + "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, ""], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, ""], + + "argparse": ["argparse@2.0.1", "", {}, ""], + + "aria-query": ["aria-query@5.3.2", "", {}, ""], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, ""], + + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, ""], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, ""], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, ""], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, ""], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, ""], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, ""], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, ""], + + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, ""], + + "async-function": ["async-function@1.0.0", "", {}, ""], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, ""], + + "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], + + "axe-core": ["axe-core@4.11.4", "", {}, ""], + + "axobject-query": ["axobject-query@4.1.0", "", {}, ""], + + "balanced-match": ["balanced-match@1.0.2", "", {}, ""], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.31", "", { "bin": "dist/cli.cjs" }, ""], + + "better-result": ["better-result@2.9.2", "", {}, "sha512-WIFoBPCdnTOdk9inkE1ZRvCZ4P0CpSkAiLlchC65N7n9DcjZ3NhqkBOlafzpOVnO8ixyi37kicmSJ3ENhPZl7Q=="], + + "brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, ""], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, ""], + + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": "cli.js" }, ""], + + "c12": ["c12@3.3.4", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.4", "defu": "^6.1.6", "dotenv": "^17.3.1", "exsolve": "^1.0.8", "giget": "^3.2.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.1.0", "pkg-types": "^2.3.0", "rc9": "^3.0.1" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA=="], + + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, ""], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, ""], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, ""], + + "callsites": ["callsites@3.1.0", "", {}, ""], + + "caniuse-lite": ["caniuse-lite@1.0.30001793", "", {}, ""], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, ""], + + "chart.js": ["chart.js@4.5.1", "", { "dependencies": { "@kurkle/color": "^0.3.0" } }, "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw=="], + + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "client-only": ["client-only@0.0.1", "", {}, ""], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, ""], + + "color-name": ["color-name@1.1.4", "", {}, ""], + + "concat-map": ["concat-map@0.0.1", "", {}, ""], + + "confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, ""], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, ""], + + "csstype": ["csstype@3.2.3", "", {}, ""], + + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, ""], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, ""], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, ""], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, ""], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, ""], + + "deep-is": ["deep-is@0.1.4", "", {}, ""], + + "deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, ""], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, ""], + + "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + + "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, ""], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, ""], + + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, ""], + + "effect": ["effect@3.20.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.360", "", {}, ""], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, ""], + + "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], + + "enhanced-resolve": ["enhanced-resolve@5.21.5", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, ""], + + "env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="], + + "es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, ""], + + "es-define-property": ["es-define-property@1.0.1", "", {}, ""], + + "es-errors": ["es-errors@1.3.0", "", {}, ""], + + "es-iterator-helpers": ["es-iterator-helpers@1.3.2", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0" } }, ""], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, ""], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, ""], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, ""], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, ""], + + "escalade": ["escalade@3.2.0", "", {}, ""], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, ""], + + "eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "bin": "bin/eslint.js" }, ""], + + "eslint-config-next": ["eslint-config-next@16.2.6", "", { "dependencies": { "@next/eslint-plugin-next": "16.2.6", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" } }, ""], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, ""], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import-x"] }, ""], + + "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, ""], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, ""], + + "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, ""], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, ""], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, ""], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, ""], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, ""], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, ""], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, ""], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, ""], + + "estraverse": ["estraverse@5.3.0", "", {}, ""], + + "esutils": ["esutils@2.0.3", "", {}, ""], + + "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], + + "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, ""], + + "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, ""], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, ""], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, ""], + + "fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="], + + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, ""], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, ""], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, ""], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, ""], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, ""], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, ""], + + "flatted": ["flatted@3.4.2", "", {}, ""], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, ""], + + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, ""], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, ""], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, ""], + + "generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, ""], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, ""], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "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" } }, ""], + + "get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, ""], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, ""], + + "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, ""], + + "giget": ["giget@3.2.0", "", { "bin": "dist/cli.mjs" }, "sha512-GvHTWcykIR/fP8cj8dMpuMMkvaeJfPvYnhq0oW+chSeIr+ldX21ifU2Ms6KBoyKZQZmVaUAAhQ2EZ68KJF8a7A=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, ""], + + "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], + + "globals": ["globals@16.4.0", "", {}, ""], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, ""], + + "gopd": ["gopd@1.2.0", "", {}, ""], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, ""], + + "grammex": ["grammex@3.1.12", "", {}, "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ=="], + + "graphmatch": ["graphmatch@1.1.1", "", {}, "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, ""], + + "has-flag": ["has-flag@4.0.0", "", {}, ""], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, ""], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, ""], + + "has-symbols": ["has-symbols@1.1.0", "", {}, ""], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, ""], + + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, ""], + + "hermes-estree": ["hermes-estree@0.25.1", "", {}, ""], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, ""], + + "hono": ["hono@4.12.21", "", {}, "sha512-uV63apnb0kyPtAUwoWgaGh9HyIFcv8lgmzPZSiTBQAFOFGIzka5EZ1dZocmGnn0XdX0+XTqJ6Tqv7selMuGLRQ=="], + + "http-status-codes": ["http-status-codes@2.3.0", "", {}, "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA=="], + + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "ignore": ["ignore@5.3.2", "", {}, ""], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, ""], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, ""], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, ""], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, ""], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, ""], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, ""], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, ""], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, ""], + + "is-callable": ["is-callable@1.2.7", "", {}, ""], + + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, ""], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, ""], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, ""], + + "is-extglob": ["is-extglob@2.1.1", "", {}, ""], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, ""], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, ""], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, ""], + + "is-map": ["is-map@2.0.3", "", {}, ""], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, ""], + + "is-number": ["is-number@7.0.0", "", {}, ""], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, ""], + + "is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, ""], + + "is-set": ["is-set@2.0.3", "", {}, ""], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, ""], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, ""], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, ""], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, ""], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, ""], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, ""], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, ""], + + "isarray": ["isarray@2.0.5", "", {}, ""], + + "isexe": ["isexe@2.0.0", "", {}, ""], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, ""], + + "jiti": ["jiti@2.7.0", "", { "bin": "lib/jiti-cli.mjs" }, ""], + + "js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, ""], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, ""], + + "jsesc": ["jsesc@3.1.0", "", { "bin": "bin/jsesc" }, ""], + + "json-buffer": ["json-buffer@3.0.1", "", {}, ""], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, ""], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, ""], + + "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": "lib/cli.js" }, ""], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, ""], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, ""], + + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, ""], + + "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, ""], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, ""], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.32.0" } }, ""], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, ""], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, ""], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, ""], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, ""], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, ""], + + "lru.min": ["lru.min@1.1.4", "", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, ""], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, ""], + + "merge2": ["merge2@1.4.1", "", {}, ""], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, ""], + + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, ""], + + "minimist": ["minimist@1.2.8", "", {}, ""], + + "ms": ["ms@2.1.3", "", {}, ""], + + "mysql2": ["mysql2@3.15.3", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg=="], + + "named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="], + + "nanoid": ["nanoid@3.3.12", "", { "bin": "bin/nanoid.cjs" }, ""], + + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": "lib/cli.js" }, ""], + + "natural-compare": ["natural-compare@1.4.0", "", {}, ""], + + "next": ["next@16.2.6", "", { "dependencies": { "@next/env": "16.2.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.6", "@next/swc-darwin-x64": "16.2.6", "@next/swc-linux-arm64-gnu": "16.2.6", "@next/swc-linux-arm64-musl": "16.2.6", "@next/swc-linux-x64-gnu": "16.2.6", "@next/swc-linux-x64-musl": "16.2.6", "@next/swc-win32-arm64-msvc": "16.2.6", "@next/swc-win32-x64-msvc": "16.2.6", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": "dist/bin/next" }, ""], + + "node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, ""], + + "node-releases": ["node-releases@2.0.44", "", {}, ""], + + "object-assign": ["object-assign@4.1.1", "", {}, ""], + + "object-inspect": ["object-inspect@1.13.4", "", {}, ""], + + "object-keys": ["object-keys@1.1.1", "", {}, ""], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, ""], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, ""], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, ""], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, ""], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, ""], + + "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], + + "openai": ["openai@6.38.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws"], "bin": "bin/cli" }, "sha512-AoMplt2UalrpgUDMh3L09QWjNRlgJPipclQvA6sYAaeF6nHNBMgmikAZGmcYLn8on4d9sQY9Q8bOLfrBS7Lc8g=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, ""], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, ""], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, ""], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, ""], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, ""], + + "path-exists": ["path-exists@4.0.0", "", {}, ""], + + "path-key": ["path-key@3.1.1", "", {}, ""], + + "path-parse": ["path-parse@1.0.7", "", {}, ""], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="], + + "pg": ["pg@8.21.0", "", { "dependencies": { "pg-connection-string": "^2.13.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.14.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA=="], + + "pg-cloudflare": ["pg-cloudflare@1.4.0", "", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="], + + "pg-connection-string": ["pg-connection-string@2.13.0", "", {}, "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig=="], + + "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], + + "pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="], + + "pg-protocol": ["pg-protocol@1.14.0", "", {}, "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA=="], + + "pg-types": ["pg-types@2.2.0", "", { "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" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + + "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], + + "picocolors": ["picocolors@1.1.1", "", {}, ""], + + "picomatch": ["picomatch@4.0.4", "", {}, ""], + + "pkg-types": ["pkg-types@2.3.1", "", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, ""], + + "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, ""], + + "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], + + "postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="], + + "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="], + + "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, ""], + + "prisma": ["prisma@7.8.0", "", { "dependencies": { "@prisma/config": "7.8.0", "@prisma/dev": "0.24.3", "@prisma/engines": "7.8.0", "@prisma/studio-core": "0.27.3", "mysql2": "3.15.3", "postgres": "3.4.7" }, "peerDependencies": { "better-sqlite3": ">=9.0.0", "typescript": ">=5.4.0" }, "optionalPeers": ["better-sqlite3"], "bin": "build/index.js" }, "sha512-yfN4yrw7HV9kEJhoy1+jgah0jafEIQsf7uWouSsM8MvJtlubsk+kM7AIBWZ8+GJl74Yj3c+nbYqBkMOxtsZ3Lw=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, ""], + + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + + "punycode": ["punycode@2.3.1", "", {}, ""], + + "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, ""], + + "rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="], + + "react": ["react@19.2.4", "", {}, ""], + + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, ""], + + "react-is": ["react-is@16.13.1", "", {}, ""], + + "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, ""], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, ""], + + "remeda": ["remeda@2.33.4", "", {}, "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "resolve": ["resolve@2.0.0-next.7", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.2", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, ""], + + "resolve-from": ["resolve-from@4.0.0", "", {}, ""], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, ""], + + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "reusify": ["reusify@1.1.0", "", {}, ""], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, ""], + + "safe-array-concat": ["safe-array-concat@1.1.4", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, ""], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, ""], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, ""], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "scheduler": ["scheduler@0.27.0", "", {}, ""], + + "semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, ""], + + "seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="], + + "server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, ""], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, ""], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, ""], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, ""], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, ""], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, ""], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, ""], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, ""], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, ""], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, ""], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, ""], + + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + + "sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, ""], + + "standardwebhooks": ["standardwebhooks@1.0.0", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg=="], + + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, ""], + + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, ""], + + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, ""], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, ""], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, ""], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, ""], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, ""], + + "strip-bom": ["strip-bom@3.0.0", "", {}, ""], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, ""], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, ""], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, ""], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, ""], + + "tailwindcss": ["tailwindcss@4.3.0", "", {}, ""], + + "tapable": ["tapable@2.3.3", "", {}, ""], + + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, ""], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, ""], + + "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, ""], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, ""], + + "tslib": ["tslib@2.8.1", "", {}, ""], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, ""], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, ""], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, ""], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, ""], + + "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, ""], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, ""], + + "typescript-eslint": ["typescript-eslint@8.59.4", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.4", "@typescript-eslint/parser": "8.59.4", "@typescript-eslint/typescript-estree": "8.59.4", "@typescript-eslint/utils": "8.59.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, ""], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, ""], + + "undici-types": ["undici-types@6.21.0", "", {}, ""], + + "unrs-resolver": ["unrs-resolver@1.12.2", "", { "dependencies": { "napi-postinstall": "^0.3.4" }, "optionalDependencies": { "@unrs/resolver-binding-darwin-arm64": "1.12.2" } }, ""], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, ""], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, ""], + + "valibot": ["valibot@1.2.0", "", { "peerDependencies": { "typescript": ">=5" } }, "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, ""], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, ""], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, ""], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, ""], + + "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, ""], + + "word-wrap": ["word-wrap@1.2.5", "", {}, ""], + + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + + "yallist": ["yallist@3.1.1", "", {}, ""], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, ""], + + "zeptomatch": ["zeptomatch@2.1.0", "", { "dependencies": { "grammex": "^3.1.11", "graphmatch": "^1.1.0" } }, "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA=="], + + "zod": ["zod@4.4.3", "", {}, ""], + + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, ""], + + "@babel/core/json5": ["json5@2.2.3", "", { "bin": "lib/cli.js" }, ""], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, ""], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, ""], + + "@prisma/engines/@prisma/get-platform": ["@prisma/get-platform@7.8.0", "", { "dependencies": { "@prisma/debug": "7.8.0" } }, "sha512-WlxgRGnolL8VH2EmkH1R/DkKNr/mVdS3G2h42IZFFZ3eUrH9OT6t73kIOSlkkrv50wG123Iq8d96ufv5LlZktw=="], + + "@prisma/fetch-engine/@prisma/get-platform": ["@prisma/get-platform@7.8.0", "", { "dependencies": { "@prisma/debug": "7.8.0" } }, "sha512-WlxgRGnolL8VH2EmkH1R/DkKNr/mVdS3G2h42IZFFZ3eUrH9OT6t73kIOSlkkrv50wG123Iq8d96ufv5LlZktw=="], + + "@prisma/get-platform/@prisma/debug": ["@prisma/debug@7.2.0", "", {}, "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw=="], + + "@prisma/streams-local/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, ""], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, ""], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.8.0", "", { "bin": "bin/semver.js" }, ""], + + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, ""], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, ""], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, ""], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, ""], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, ""], + + "is-bun-module/semver": ["semver@7.8.0", "", { "bin": "bin/semver.js" }, ""], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, ""], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, ""], + + "pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "sharp/semver": ["semver@7.8.0", "", { "bin": "bin/semver.js" }, ""], + + "@prisma/streams-local/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, ""], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, ""], + } +} diff --git a/data/task-templates.json b/data/task-templates.json new file mode 100644 index 0000000..a2f7724 --- /dev/null +++ b/data/task-templates.json @@ -0,0 +1,107 @@ +[ + { + "type": "attention_training", + "difficulty": 1, + "duration": 3, + "description": "Breath counting: Count each exhale from 1 to 10, then start over. If your mind wanders, gently return to 1.", + "reason": "Foundational attention exercise. Builds the skill of noticing when attention drifts and bringing it back without judgment." + }, + { + "type": "attention_training", + "difficulty": 2, + "duration": 5, + "description": "Breath at the nostrils: Focus on the sensation of air passing the tip of your nose. Notice temperature, texture, and rhythm without changing anything.", + "reason": "Narrower focus point than breath counting. Trains precision of attention by anchoring to a subtle physical sensation." + }, + { + "type": "attention_training", + "difficulty": 3, + "duration": 10, + "description": "Open awareness: Sit quietly and let your attention rest on whatever arises — sounds, body sensations, thoughts. Don't cling or reject. Just notice.", + "reason": "Advanced attention exercise. Builds meta-awareness — the ability to observe the mind's movements without being carried away by them." + }, + { + "type": "emotion_awareness", + "difficulty": 1, + "duration": 3, + "description": "Name the weather: Close your eyes and ask 'What's the emotional weather right now?' Label it simply — sunny, cloudy, stormy, still. Don't explain why. Just name it.", + "reason": "Builds the vocabulary and habit of checking in with emotional state. Naming emotions reduces their intensity and creates space for choice." + }, + { + "type": "emotion_awareness", + "difficulty": 2, + "duration": 5, + "description": "Body scan: Slowly move attention from the top of your head to your toes. At each body part, notice any sensation — tightness, warmth, buzzing, emptiness. Don't try to change anything.", + "reason": "Emotions live in the body before they reach conscious awareness. Body scanning builds the sensory vocabulary that makes emotional signals legible." + }, + { + "type": "emotion_awareness", + "difficulty": 3, + "duration": 7, + "description": "Emotion tracking: Set a timer for 7 minutes. Every 60 seconds, pause and write one word describing your current emotional state. Notice how emotions shift moment to moment.", + "reason": "Reveals the impermanent nature of emotions. Watching emotions change undermines the belief that 'this feeling will last forever.'" + }, + { + "type": "stress_regulation", + "difficulty": 1, + "duration": 3, + "description": "4-4-6 breathing: Inhale for 4 counts, hold for 4, exhale for 6. The long exhale activates the parasympathetic nervous system.", + "reason": "Physiological stress reduction through breath pattern. The extended exhale is one of the fastest ways to signal safety to the nervous system." + }, + { + "type": "stress_regulation", + "difficulty": 2, + "duration": 5, + "description": "Progressive relaxation: Tense each muscle group for 5 seconds, then release for 10. Start with feet, work up to face. Notice the contrast between tension and release.", + "reason": "Teaches the difference between tension and relaxation at the body level. Many people don't realize they're holding tension until they deliberately release it." + }, + { + "type": "stress_regulation", + "difficulty": 3, + "duration": 8, + "description": "Grounding through senses: Name 5 things you can see, 4 things you can touch, 3 things you can hear, 2 things you can smell, 1 thing you can taste.", + "reason": "Full sensory grounding anchors you in the present moment. Effective for acute stress by redirecting attention from internal worry to external reality." + }, + { + "type": "cognitive_clarity", + "difficulty": 1, + "duration": 3, + "description": "Thought labeling: Sit quietly and watch thoughts arise. Label each one silently — 'planning,' 'worrying,' 'remembering,' 'judging.' Don't engage the thought. Just label and release.", + "reason": "Creates distance between you and your thoughts. Labeling activates the prefrontal cortex and reduces amygdala reactivity." + }, + { + "type": "cognitive_clarity", + "difficulty": 2, + "duration": 5, + "description": "Reframing practice: Think of a mildly frustrating situation today. Write down your automatic thought, then write three alternative interpretations that are equally plausible.", + "reason": "Cognitive reframing is the core skill of cognitive flexibility. Practicing with mild frustrations builds the muscle for harder situations." + }, + { + "type": "cognitive_clarity", + "difficulty": 3, + "duration": 10, + "description": "Inner dialogue observation: For 10 minutes, write down every thought that passes through your mind as if transcribing a conversation. Don't edit or judge. Then read it back. Notice which 'voice' is speaking — the critic? The planner? The child?", + "reason": "Reveals the multiplicity of the inner world. Recognizing your inner voices is the first step toward choosing which ones to amplify." + }, + { + "type": "integration", + "difficulty": 1, + "duration": 3, + "description": "One-minute pause: Three times today, pause for 60 seconds. Take three conscious breaths. Notice what's happening in your body and mind. Then continue.", + "reason": "Integrates practice into daily life. Short, frequent pauses build the habit of mindful awareness without requiring dedicated sitting time." + }, + { + "type": "integration", + "difficulty": 2, + "duration": 5, + "description": "Mindful routine: Pick one daily activity (brushing teeth, washing dishes, walking to your desk). Do it with full attention today. Notice every sensation, movement, and sound.", + "reason": "Turns everyday activities into practice opportunities. The goal isn't more practice time — it's weaving practice into time you already have." + }, + { + "type": "integration", + "difficulty": 3, + "duration": 10, + "description": "Difficult conversation practice: Before a conversation that might be challenging, sit for 5 minutes and set an intention. During the conversation, notice your reactions without acting on them. After, reflect for 5 minutes on what you noticed.", + "reason": "The ultimate integration — bringing awareness into interpersonal dynamics. This is where practice meets real life." + } +] diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..05e726d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..c8d25cb --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "inner-os", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "@clerk/nextjs": "^7.3.7", + "@prisma/adapter-pg": "^7.8.0", + "@prisma/client": "^7.8.0", + "next": "16.2.6", + "openai": "^6.38.0", + "react": "19.2.4", + "react-dom": "19.2.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20.19.41", + "@types/react": "^19", + "@types/react-dom": "^19", + "dotenv": "^17.4.2", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "prisma": "^7.8.0", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/prisma.config.ts b/prisma.config.ts new file mode 100644 index 0000000..831a20f --- /dev/null +++ b/prisma.config.ts @@ -0,0 +1,14 @@ +// This file was generated by Prisma, and assumes you have installed the following: +// npm install --save-dev prisma dotenv +import "dotenv/config"; +import { defineConfig } from "prisma/config"; + +export default defineConfig({ + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations", + }, + datasource: { + url: process.env["DATABASE_URL"], + }, +}); diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..0ebda23 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,126 @@ +generator client { + provider = "prisma-client" + output = "../src/generated/prisma" +} + +datasource db { + provider = "postgresql" +} + +enum TaskType { + attention_training + emotion_awareness + stress_regulation + cognitive_clarity + integration +} + +enum TaskStatus { + pending + completed + expired +} + +enum RewardEventType { + daily_completion + streak_3 + streak_7 + streak_14 + streak_30 + stage_up + first_session +} + +model User { + id String @id // Clerk user ID + email String @unique + createdAt DateTime @default(now()) @map("created_at") + + state UserState? @relation("UserState") + sessions Session[] + tasks Task[] + rewardEvents RewardEvent[] + weeklySummary WeeklySummary? + + @@map("users") +} + +model UserState { + id String @id @default(uuid()) + userId String @unique @map("user_id") + attentionLevel Int @default(50) @map("attention_level") + emotionStability Int @default(50) @map("emotion_stability") + stressLevel Int @default(50) @map("stress_level") + practiceStage Int @default(0) @map("practice_stage") + consistencyScore Int @default(0) @map("consistency_score") + streakDays Int @default(0) @map("streak_days") + totalSessionsCompleted Int @default(0) @map("total_sessions_completed") + onboardingCompleted Boolean @default(false) @map("onboarding_completed") + lastSessionDate DateTime? @map("last_session_date") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation("UserState", fields: [userId], references: [id], onDelete: Cascade) + + @@map("user_states") +} + +model Session { + id String @id @default(uuid()) + userId String @map("user_id") + taskType TaskType @map("task_type") + taskDescription String @map("task_description") + durationSeconds Int @default(300) @map("duration_seconds") + completed Boolean @default(false) + focusScore Int? @map("focus_score") + emotionScore Int? @map("emotion_score") + notes String? + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId, createdAt]) + @@map("sessions") +} + +model Task { + id String @id @default(uuid()) + userId String @map("user_id") + type TaskType + difficulty Int @default(1) + duration Int @default(5) + description String + reasonJson String @default("{}") @map("reason_json") + status TaskStatus @default(pending) + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId, createdAt]) + @@index([userId, status]) + @@map("tasks") +} + +model RewardEvent { + id String @id @default(uuid()) + userId String @map("user_id") + type RewardEventType + message String + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId, createdAt]) + @@map("reward_events") +} + +model WeeklySummary { + id String @id @default(uuid()) + userId String @unique @map("user_id") + summary String? + weekStart DateTime @map("week_start") + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("weekly_summaries") +} diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/api/cron/weekly-summaries/route.ts b/src/app/api/cron/weekly-summaries/route.ts new file mode 100644 index 0000000..e109632 --- /dev/null +++ b/src/app/api/cron/weekly-summaries/route.ts @@ -0,0 +1,40 @@ +import { NextResponse } from "next/server"; +import OpenAI from "openai"; +import { generateAllWeeklySummaries } from "@/lib/engines/summary-generator"; + +export async function POST(req: Request) { + // Verify cron secret + const authHeader = req.headers.get("authorization"); + const expectedSecret = process.env.CRON_SECRET; + + if (!expectedSecret || expectedSecret === "change-me-to-a-random-string") { + return NextResponse.json( + { error: "CRON_SECRET not configured" }, + { status: 500 } + ); + } + + if (!authHeader || authHeader !== `Bearer ${expectedSecret}`) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); + + try { + const results = await generateAllWeeklySummaries(openai); + const succeeded = results.filter((r) => r.success).length; + const failed = results.filter((r) => !r.success).length; + + return NextResponse.json({ + processed: results.length, + succeeded, + failed, + }); + } catch (error) { + console.error("Weekly summary cron failed:", error); + return NextResponse.json( + { error: "Cron job failed" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/rewards/route.ts b/src/app/api/rewards/route.ts new file mode 100644 index 0000000..443e89c --- /dev/null +++ b/src/app/api/rewards/route.ts @@ -0,0 +1,25 @@ +import { auth } from "@clerk/nextjs/server"; +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/prisma"; + +export async function GET(req: Request) { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + const { searchParams } = new URL(req.url); + const limit = Math.min(parseInt(searchParams.get("limit") || "20", 10), 50); + + const rewards = await prisma.rewardEvent.findMany({ + where: { userId }, + orderBy: { createdAt: "desc" }, + take: limit, + select: { + id: true, + type: true, + message: true, + createdAt: true, + }, + }); + + return NextResponse.json({ rewards }); +} diff --git a/src/app/api/session/complete/route.ts b/src/app/api/session/complete/route.ts new file mode 100644 index 0000000..687aeed --- /dev/null +++ b/src/app/api/session/complete/route.ts @@ -0,0 +1,167 @@ +import { auth } from "@clerk/nextjs/server"; +import { NextResponse } from "next/server"; +import OpenAI from "openai"; +import { prisma } from "@/lib/prisma"; +import { processReflection } from "@/lib/engines/reflection-analyzer"; +import { computeStateUpdate } from "@/lib/engines/progress-tracker"; +import { generateRewards, persistRewards } from "@/lib/engines/reward-generator"; + +export async function POST(req: Request) { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + const body = await req.json().catch(() => null); + if (!body || typeof body !== "object") { + return NextResponse.json({ error: "Invalid request body" }, { status: 400 }); + } + + const { taskId, notes, durationSeconds } = body as { + taskId?: string; + notes?: string; + durationSeconds?: number; + }; + + if (!taskId) { + return NextResponse.json({ error: "taskId is required" }, { status: 400 }); + } + + // Verify the task belongs to this user and is pending + const task = await prisma.task.findFirst({ + where: { id: taskId, userId, status: "pending" }, + }); + + if (!task) { + return NextResponse.json( + { error: "Task not found or already completed" }, + { status: 404 } + ); + } + + // Get current state + const currentState = await prisma.userState.findUnique({ + where: { userId }, + }); + + if (!currentState) { + return NextResponse.json( + { error: "User state not found. Complete onboarding first." }, + { status: 400 } + ); + } + + const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); + + // Process reflection through LLM #2 + const reflection = await processReflection( + { + notes: notes || "", + durationSeconds: durationSeconds || task.duration * 60, + taskType: task.type, + taskDescription: task.description, + }, + { + attentionLevel: currentState.attentionLevel, + emotionStability: currentState.emotionStability, + stressLevel: currentState.stressLevel, + }, + openai + ); + + // Get recent session dates for consistency computation + const recentSessions = await prisma.session.findMany({ + where: { userId }, + orderBy: { createdAt: "desc" }, + take: 14, + select: { createdAt: true }, + }); + + // Compute state update + const stateUpdate = computeStateUpdate( + currentState, + { + attentionDelta: reflection.attentionDelta, + emotionDelta: reflection.emotionDelta, + stressDelta: reflection.stressDelta, + }, + recentSessions.map((s) => s.createdAt) + ); + + // Generate rewards + const rewards = generateRewards({ + isFirstSession: currentState.totalSessionsCompleted === 0, + streakMilestone: stateUpdate.streakMilestone, + streakDays: stateUpdate.streakDays, + stageAdvanced: stateUpdate.stageAdvanced, + newStage: stateUpdate.practiceStage, + totalSessionsCompleted: stateUpdate.totalSessionsCompleted, + }); + + // Perform all DB writes in a transaction + await prisma.$transaction([ + // Mark task as completed + prisma.task.update({ + where: { id: taskId }, + data: { status: "completed" }, + }), + + // Create session record + prisma.session.create({ + data: { + userId, + taskType: task.type, + taskDescription: task.description, + durationSeconds: durationSeconds || task.duration * 60, + completed: true, + focusScore: reflection.focusScore, + emotionScore: reflection.emotionScore, + notes: notes || null, + }, + }), + + // Update user state + prisma.userState.update({ + where: { userId }, + data: { + attentionLevel: stateUpdate.attentionLevel, + emotionStability: stateUpdate.emotionStability, + stressLevel: stateUpdate.stressLevel, + practiceStage: stateUpdate.practiceStage, + consistencyScore: stateUpdate.consistencyScore, + streakDays: stateUpdate.streakDays, + totalSessionsCompleted: stateUpdate.totalSessionsCompleted, + lastSessionDate: new Date(), + }, + }), + + // Persist rewards + ...rewards.map((r) => + prisma.rewardEvent.create({ + data: { + userId, + type: r.type, + message: r.message, + }, + }) + ), + ]); + + return NextResponse.json({ + reflection: { + focusScore: reflection.focusScore, + emotionScore: reflection.emotionScore, + insight: reflection.insight, + }, + state: { + attentionLevel: stateUpdate.attentionLevel, + emotionStability: stateUpdate.emotionStability, + stressLevel: stateUpdate.stressLevel, + practiceStage: stateUpdate.practiceStage, + consistencyScore: stateUpdate.consistencyScore, + streakDays: stateUpdate.streakDays, + totalSessionsCompleted: stateUpdate.totalSessionsCompleted, + }, + rewards, + stageAdvanced: stateUpdate.stageAdvanced, + streakMilestone: stateUpdate.streakMilestone, + }); +} diff --git a/src/app/api/summary/weekly/route.ts b/src/app/api/summary/weekly/route.ts new file mode 100644 index 0000000..6005fdc --- /dev/null +++ b/src/app/api/summary/weekly/route.ts @@ -0,0 +1,24 @@ +import { auth } from "@clerk/nextjs/server"; +import { NextResponse } from "next/server"; +import OpenAI from "openai"; +import { prisma } from "@/lib/prisma"; + +export async function GET() { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + const summary = await prisma.weeklySummary.findUnique({ + where: { userId }, + select: { + summary: true, + weekStart: true, + createdAt: true, + }, + }); + + if (!summary) { + return NextResponse.json({ summary: null }); + } + + return NextResponse.json({ summary }); +} diff --git a/src/app/api/task/daily/route.ts b/src/app/api/task/daily/route.ts new file mode 100644 index 0000000..2219fcd --- /dev/null +++ b/src/app/api/task/daily/route.ts @@ -0,0 +1,49 @@ +import { auth } from "@clerk/nextjs/server"; +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/prisma"; + +export async function GET() { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const task = await prisma.task.findFirst({ + where: { + userId, + status: "pending", + createdAt: { gte: today }, + }, + orderBy: { createdAt: "desc" }, + select: { + id: true, + type: true, + difficulty: true, + duration: true, + description: true, + reasonJson: true, + status: true, + createdAt: true, + }, + }); + + if (!task) { + return NextResponse.json({ task: null }); + } + + let reason = ""; + try { + reason = JSON.parse(task.reasonJson).reason || ""; + } catch { + // reasonJson is unparseable, leave empty + } + + return NextResponse.json({ + task: { + ...task, + reason, + reasonJson: undefined, + }, + }); +} diff --git a/src/app/api/task/generate/route.ts b/src/app/api/task/generate/route.ts new file mode 100644 index 0000000..1e1b6d8 --- /dev/null +++ b/src/app/api/task/generate/route.ts @@ -0,0 +1,27 @@ +import { auth } from "@clerk/nextjs/server"; +import { NextResponse } from "next/server"; +import OpenAI from "openai"; +import { generateDailyTask } from "@/lib/engines/task-selector"; + +export async function POST() { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); + + try { + const result = await generateDailyTask(userId, openai); + + return NextResponse.json({ + task: result.task, + fromTemplate: result.fromTemplate, + safe: result.safety.safe, + }); + } catch (error) { + console.error("Task generation failed:", error); + return NextResponse.json( + { error: "Failed to generate task" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/user/onboarding/route.ts b/src/app/api/user/onboarding/route.ts new file mode 100644 index 0000000..18765f1 --- /dev/null +++ b/src/app/api/user/onboarding/route.ts @@ -0,0 +1,32 @@ +import { auth } from "@clerk/nextjs/server"; +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/prisma"; + +export async function POST() { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + // Mark onboarding complete + const state = await prisma.userState.upsert({ + where: { userId }, + create: { + userId, + onboardingCompleted: true, + }, + update: { + onboardingCompleted: true, + }, + select: { + attentionLevel: true, + emotionStability: true, + stressLevel: true, + practiceStage: true, + consistencyScore: true, + streakDays: true, + totalSessionsCompleted: true, + onboardingCompleted: true, + }, + }); + + return NextResponse.json({ state }); +} diff --git a/src/app/api/user/state/route.ts b/src/app/api/user/state/route.ts new file mode 100644 index 0000000..af9d16a --- /dev/null +++ b/src/app/api/user/state/route.ts @@ -0,0 +1,71 @@ +import { auth } from "@clerk/nextjs/server"; +import { NextResponse } from "next/server"; +import { prisma } from "@/lib/prisma"; + +export async function GET() { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + const state = await prisma.userState.findUnique({ + where: { userId }, + select: { + attentionLevel: true, + emotionStability: true, + stressLevel: true, + practiceStage: true, + consistencyScore: true, + streakDays: true, + totalSessionsCompleted: true, + onboardingCompleted: true, + lastSessionDate: true, + }, + }); + + if (!state) { + return NextResponse.json({ + state: null, + hasStarted: false, + }); + } + + return NextResponse.json({ + state, + hasStarted: true, + }); +} + +export async function PATCH(req: Request) { + const { userId } = await auth(); + if (!userId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + const body = await req.json().catch(() => null); + if (!body || typeof body !== "object") { + return NextResponse.json({ error: "Invalid request body" }, { status: 400 }); + } + + const { onboardingCompleted } = body as { onboardingCompleted?: boolean }; + + const state = await prisma.userState.upsert({ + where: { userId }, + create: { + userId, + onboardingCompleted: onboardingCompleted ?? false, + }, + update: { + ...(onboardingCompleted !== undefined ? { onboardingCompleted } : {}), + }, + select: { + attentionLevel: true, + emotionStability: true, + stressLevel: true, + practiceStage: true, + consistencyScore: true, + streakDays: true, + totalSessionsCompleted: true, + onboardingCompleted: true, + lastSessionDate: true, + }, + }); + + return NextResponse.json({ state }); +} diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..f01f90c --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,46 @@ +@import "tailwindcss"; + +@theme inline { + --color-cream: #FAF7F2; + --color-sand: #F3EFE8; + --color-stone: #E6E0D4; + --color-amber: #C8834A; + --color-sage: #7A9A8B; + --color-ink: #2C2416; + --color-muted: #6B5E4A; + --color-faded: #9B8E7A; + --color-success: #7A9A8B; + --color-warning: #C8834A; + --color-error: #C45A4A; + --color-info: #5A8AB5; + --font-display: var(--font-fraunces), Georgia, serif; + --font-body: var(--font-dm-sans), system-ui, sans-serif; +} + +:root { + --background: #FAF7F2; + --foreground: #2C2416; + --surface: #F3EFE8; + --border: #E6E0D4; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #1E1A14; + --foreground: #F3EFE8; + --surface: #292418; + --border: #3D3528; + --color-muted: #B8A892; + --color-faded: #8A7D6B; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: var(--font-body); + font-size: 16px; + line-height: 1.6; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..1fb18d8 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,44 @@ +import type { Metadata } from "next"; +import { ClerkProvider } from "@clerk/nextjs"; +import { DM_Sans, Fraunces } from "next/font/google"; +import TabBar from "@/components/TabBar"; +import "./globals.css"; + +const fraunces = Fraunces({ + variable: "--font-fraunces", + subsets: ["latin"], + axes: ["opsz", "SOFT", "WONK"], +}); + +const dmSans = DM_Sans({ + variable: "--font-dm-sans", + subsets: ["latin"], + weight: ["400", "500", "700"], +}); + +export const metadata: Metadata = { + title: "Inner OS", + description: "Your private inner development practice.", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + +
+ {children} +
+ + + +
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..30c63f8 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,265 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useUser } from "@clerk/nextjs"; +import { useRouter } from "next/navigation"; +import StatBar from "@/components/StatBar"; + +interface StateData { + attentionLevel: number; + emotionStability: number; + stressLevel: number; + practiceStage: number; + consistencyScore: number; + streakDays: number; + totalSessionsCompleted: number; + onboardingCompleted: boolean; +} + +interface TaskData { + id: string; + type: string; + difficulty: number; + duration: number; + description: string; + reason: string; +} + +export default function HomePage() { + const { user, isLoaded: userLoaded } = useUser(); + const router = useRouter(); + const [state, setState] = useState(null); + const [task, setTask] = useState(null); + const [loading, setLoading] = useState(true); + const [generating, setGenerating] = useState(false); + const [genError, setGenError] = useState(null); + + useEffect(() => { + if (!userLoaded) return; + Promise.all([ + fetch("/api/user/state").then((r) => r.json()), + fetch("/api/task/daily").then((r) => r.json()), + ]) + .then(([stateJson, taskJson]) => { + setState(stateJson.state); + setTask(taskJson.task || null); + }) + .catch(() => {}) + .finally(() => setLoading(false)); + }, [userLoaded]); + + const handleGenerate = async () => { + setGenerating(true); + setGenError(null); + try { + const res = await fetch("/api/task/generate", { method: "POST" }); + if (!res.ok) { + const json = await res.json(); + throw new Error(json.error || "Failed to generate task"); + } + const json = await res.json(); + setTask(json.task); + } catch (e) { + setGenError(e instanceof Error ? e.message : "Something went wrong"); + } finally { + setGenerating(false); + } + }; + + if (!userLoaded || loading) { + return ( +
+
Loading...
+
+ ); + } + + if (!user) return null; + + // Redirect to settings for onboarding + if (state && !state.onboardingCompleted) { + return ( +
+ +
+
+ +
+
+

+ One step left +

+

+ Complete onboarding in Settings to start your practice. +

+
+ +
+
+ ); + } + + const metrics = state + ? [ + { label: "Attention", value: state.attentionLevel, color: "amber" as const }, + { label: "Emotion", value: state.emotionStability, color: "sage" as const }, + { label: "Stress", value: state.stressLevel, color: "warning" as const }, + { label: "Consistency", value: state.consistencyScore, color: "info" as const }, + ] + : []; + + return ( +
+ + + {/* State overview */} + {state && ( +
+
+

+ Streak +

+

+ {state.streakDays} +

+

days

+
+
+

+ Sessions +

+

+ {state.totalSessionsCompleted} +

+

total

+
+
+ )} + + {/* Core metrics */} + {state && ( +
+

+ Core metrics +

+ {metrics.map((m) => ( + + ))} +
+ )} + + {/* Today's task */} +
+

+ Today’s practice +

+ + {task ? ( +
+
+ + {formatTaskType(task.type)} + + + {formatDifficulty(task.difficulty)} · {task.duration}m + +
+
+

+ {formatTaskType(task.type)} practice +

+

+ {task.description} +

+
+ {task.reason && ( +
+ {task.reason} +
+ )} + +
+ ) : ( +
+

+ No task for today yet. Generate one to start your practice. +

+ {genError && ( +

{genError}

+ )} + +
+ )} +
+
+ ); +} + +function Greeting({ firstName }: { firstName: string | null }) { + const hour = new Date().getHours(); + const greeting = hour < 12 ? "Good morning" : hour < 18 ? "Good afternoon" : "Good evening"; + const name = firstName || "there"; + + return ( +
+

+ {greeting}, {name} +

+

Your practice is waiting.

+
+ ); +} + +function formatTaskType(type: string): string { + const labels: Record = { + attention_training: "Attention", + emotion_awareness: "Emotion", + stress_regulation: "Stress", + cognitive_clarity: "Clarity", + integration: "Integration", + }; + return labels[type] ?? type; +} + +function formatDifficulty(difficulty: number): string { + const labels: Record = { + 1: "Beginner", + 2: "Intermediate", + 3: "Advanced", + }; + return labels[difficulty] ?? String(difficulty); +} diff --git a/src/app/practice/page.tsx b/src/app/practice/page.tsx new file mode 100644 index 0000000..df79627 --- /dev/null +++ b/src/app/practice/page.tsx @@ -0,0 +1,216 @@ +"use client"; + +import { Suspense, useEffect, useState, useCallback } from "react"; +import { useSearchParams, useRouter } from "next/navigation"; + +interface TaskData { + id: string; + type: string; + difficulty: number; + duration: number; + description: string; + reason: string; +} + +export default function PracticePage() { + return ( + }> + + + ); +} + +function LoadingFallback() { + return ( +
+
Loading...
+
+ ); +} + +function PracticeContent() { + const searchParams = useSearchParams(); + const router = useRouter(); + const taskId = searchParams.get("id"); + + const [task, setTask] = useState(null); + const [loading, setLoading] = useState(true); + const [phase, setPhase] = useState<"ready" | "active" | "done">("ready"); + const [secondsRemaining, setSecondsRemaining] = useState(0); + const [elapsedSeconds, setElapsedSeconds] = useState(0); + + useEffect(() => { + if (!taskId) { + router.push("/"); + return; + } + + fetch("/api/task/daily") + .then((r) => r.json()) + .then((json) => { + if (json.task && json.task.id === taskId) { + setTask(json.task); + setSecondsRemaining(json.task.duration * 60); + } else { + router.push("/"); + } + }) + .catch(() => router.push("/")) + .finally(() => setLoading(false)); + }, [taskId, router]); + + // Timer + useEffect(() => { + if (phase !== "active") return; + if (secondsRemaining <= 0) { + setPhase("done"); + return; + } + const interval = setInterval(() => { + setSecondsRemaining((s) => s - 1); + setElapsedSeconds((s) => s + 1); + }, 1000); + return () => clearInterval(interval); + }, [phase, secondsRemaining]); + + const handleStart = () => setPhase("active"); + + const handleFinish = () => { + router.push( + `/reflect?taskId=${taskId}&duration=${elapsedSeconds}&type=${task?.type}&description=${encodeURIComponent(task?.description || "")}` + ); + }; + + if (loading) { + return ( +
+
Loading...
+
+ ); + } + + if (!task) return null; + + const minutes = Math.floor(secondsRemaining / 60); + const secs = secondsRemaining % 60; + const progressPct = + task.duration > 0 + ? Math.min(100, Math.round((elapsedSeconds / (task.duration * 60)) * 100)) + : 0; + + return ( +
+ {/* Back */} + + + {/* Task display */} +
+
+ + {formatTaskType(task.type)} + +

+ Your practice +

+
+ +
+

{task.description}

+

+ Why this task: {task.reason} +

+
+
+ + {/* Timer */} +
+ {phase === "ready" && ( + + )} + + {phase === "active" && ( +
+
+ {/* Progress ring */} + + + {String(minutes).padStart(2, "0")}:{String(secs).padStart(2, "0")} + +
+ +
+ )} + + {phase === "done" && ( +
+
+ +
+

Practice complete

+ +
+ )} +
+
+ ); +} + +function formatTaskType(type: string): string { + const labels: Record = { + attention_training: "Attention", + emotion_awareness: "Emotion", + stress_regulation: "Stress", + cognitive_clarity: "Clarity", + integration: "Integration", + }; + return labels[type] ?? type; +} diff --git a/src/app/progress/page.tsx b/src/app/progress/page.tsx new file mode 100644 index 0000000..16972be --- /dev/null +++ b/src/app/progress/page.tsx @@ -0,0 +1,130 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useUser } from "@clerk/nextjs"; +import StatBar from "@/components/StatBar"; +import StageBadge from "@/components/StageBadge"; + +interface StateData { + attentionLevel: number; + emotionStability: number; + stressLevel: number; + practiceStage: number; + consistencyScore: number; + streakDays: number; + totalSessionsCompleted: number; +} + +export default function ProgressPage() { + const { isLoaded } = useUser(); + const [state, setState] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (!isLoaded) return; + fetch("/api/user/state") + .then((r) => r.json()) + .then((json) => setState(json.state)) + .catch(() => {}) + .finally(() => setLoading(false)); + }, [isLoaded]); + + if (loading) { + return ( +
+
Loading...
+
+ ); + } + + if (!state) { + return ( +
+

Complete onboarding to see your progress.

+
+ ); + } + + const metrics = [ + { label: "Attention", value: state.attentionLevel, color: "amber" as const }, + { label: "Emotional Stability", value: state.emotionStability, color: "sage" as const }, + { label: "Stress Level", value: state.stressLevel, color: "warning" as const }, + { label: "Consistency", value: state.consistencyScore, color: "info" as const }, + ]; + + return ( +
+
+

+ Your progress +

+

+ Stage +

+
+ + {/* Stats grid */} +
+
+

+ Streak +

+

+ {state.streakDays} +

+

days

+
+
+

+ Sessions +

+

+ {state.totalSessionsCompleted} +

+

total

+
+
+ + {/* Metric bars */} +
+

+ Core metrics +

+ {metrics.map((m) => ( + + ))} +
+ + {/* Stage info */} +
+

+ Stage progress +

+
+ {[0, 1, 2, 3, 4].map((s) => ( +
+ ))} +
+
+ Beginner + Foundation + Growing + Established + Advanced +
+
+
+ ); +} diff --git a/src/app/reflect/page.tsx b/src/app/reflect/page.tsx new file mode 100644 index 0000000..c6335b2 --- /dev/null +++ b/src/app/reflect/page.tsx @@ -0,0 +1,198 @@ +"use client"; + +import { useState, Suspense } from "react"; +import { useSearchParams, useRouter } from "next/navigation"; + +function ReflectForm() { + const searchParams = useSearchParams(); + const router = useRouter(); + + const taskId = searchParams.get("taskId") || ""; + const duration = parseInt(searchParams.get("duration") || "0", 10); + const taskType = searchParams.get("type") || ""; + const taskDescription = searchParams.get("description") || ""; + + const [notes, setNotes] = useState(""); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(null); + const [result, setResult] = useState<{ + focusScore: number; + emotionScore: number; + insight: string; + } | null>(null); + + const handleSubmit = async () => { + if (!taskId) return; + setSubmitting(true); + setError(null); + + try { + const res = await fetch("/api/session/complete", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + taskId, + notes, + durationSeconds: duration, + }), + }); + + if (!res.ok) { + const json = await res.json(); + throw new Error(json.error || "Failed to save reflection"); + } + + const json = await res.json(); + setResult(json.reflection); + } catch (e) { + setError(e instanceof Error ? e.message : "Something went wrong"); + } finally { + setSubmitting(false); + } + }; + + if (!taskId) { + return ( +
+ +
+ ); + } + + // After successful submission + if (result) { + return ( +
+
+
+ +
+

+ Practice complete +

+
+ +
+

+ “{result.insight}” +

+
+ + + {result.focusScore}/5 + {" "} + focus + + + + {result.emotionScore}/5 + {" "} + emotional awareness + +
+
+ + +
+ ); + } + + return ( +
+
+ +

+ Reflect on your practice +

+

+ {formatTaskType(taskType)} · {Math.floor(duration / 60)}m{" "} + {duration % 60}s +

+
+ +
+ +